clawup 1.0.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/README.md +245 -0
- package/dist/adapters/api-adapter.d.ts +76 -0
- package/dist/adapters/api-adapter.js +250 -0
- package/dist/adapters/api-adapter.js.map +1 -0
- package/dist/adapters/cli-adapter.d.ts +15 -0
- package/dist/adapters/cli-adapter.js +208 -0
- package/dist/adapters/cli-adapter.js.map +1 -0
- package/dist/adapters/index.d.ts +22 -0
- package/dist/adapters/index.js +32 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/types.d.ts +135 -0
- package/dist/adapters/types.js +14 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/bin.d.ts +8 -0
- package/dist/bin.js +221 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/config.d.ts +21 -0
- package/dist/commands/config.js +323 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/deploy.d.ts +7 -0
- package/dist/commands/deploy.js +13 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/destroy.d.ts +7 -0
- package/dist/commands/destroy.js +13 -0
- package/dist/commands/destroy.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +698 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +8 -0
- package/dist/commands/list.js +42 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/push.d.ts +7 -0
- package/dist/commands/push.js +19 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/redeploy.d.ts +7 -0
- package/dist/commands/redeploy.js +13 -0
- package/dist/commands/redeploy.js.map +1 -0
- package/dist/commands/secrets.d.ts +16 -0
- package/dist/commands/secrets.js +169 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/ssh.d.ts +9 -0
- package/dist/commands/ssh.js +108 -0
- package/dist/commands/ssh.js.map +1 -0
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.js +13 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.js +126 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.d.ts +7 -0
- package/dist/commands/validate.js +13 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/webhooks.d.ts +7 -0
- package/dist/commands/webhooks.js +13 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/lib/__tests__/identity.test.d.ts +1 -0
- package/dist/lib/__tests__/identity.test.js +186 -0
- package/dist/lib/__tests__/identity.test.js.map +1 -0
- package/dist/lib/__tests__/validate-agent.test.d.ts +1 -0
- package/dist/lib/__tests__/validate-agent.test.js +38 -0
- package/dist/lib/__tests__/validate-agent.test.js.map +1 -0
- package/dist/lib/config.d.ts +69 -0
- package/dist/lib/config.js +218 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +29 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/exec.d.ts +24 -0
- package/dist/lib/exec.js +63 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/prerequisites.d.ts +8 -0
- package/dist/lib/prerequisites.js +146 -0
- package/dist/lib/prerequisites.js.map +1 -0
- package/dist/lib/process.d.ts +18 -0
- package/dist/lib/process.js +37 -0
- package/dist/lib/process.js.map +1 -0
- package/dist/lib/pulumi.d.ts +37 -0
- package/dist/lib/pulumi.js +87 -0
- package/dist/lib/pulumi.js.map +1 -0
- package/dist/lib/tailscale.d.ts +75 -0
- package/dist/lib/tailscale.js +251 -0
- package/dist/lib/tailscale.js.map +1 -0
- package/dist/lib/tool-helpers.d.ts +15 -0
- package/dist/lib/tool-helpers.js +35 -0
- package/dist/lib/tool-helpers.js.map +1 -0
- package/dist/lib/ui.d.ts +26 -0
- package/dist/lib/ui.js +86 -0
- package/dist/lib/ui.js.map +1 -0
- package/dist/lib/update-check.d.ts +8 -0
- package/dist/lib/update-check.js +151 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/lib/vendor.d.ts +34 -0
- package/dist/lib/vendor.js +128 -0
- package/dist/lib/vendor.js.map +1 -0
- package/dist/lib/workspace.d.ts +21 -0
- package/dist/lib/workspace.js +170 -0
- package/dist/lib/workspace.js.map +1 -0
- package/dist/tools/deploy.d.ts +16 -0
- package/dist/tools/deploy.js +181 -0
- package/dist/tools/deploy.js.map +1 -0
- package/dist/tools/destroy.d.ts +16 -0
- package/dist/tools/destroy.js +119 -0
- package/dist/tools/destroy.js.map +1 -0
- package/dist/tools/index.d.ts +20 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/push.d.ts +29 -0
- package/dist/tools/push.js +341 -0
- package/dist/tools/push.js.map +1 -0
- package/dist/tools/redeploy.d.ts +17 -0
- package/dist/tools/redeploy.js +181 -0
- package/dist/tools/redeploy.js.map +1 -0
- package/dist/tools/status.d.ts +16 -0
- package/dist/tools/status.js +205 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/validate.d.ts +16 -0
- package/dist/tools/validate.js +219 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/webhooks.d.ts +17 -0
- package/dist/tools/webhooks.js +181 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/infra/Pulumi.yaml +6 -0
- package/infra/dist/components/cloud-init.js +412 -0
- package/infra/dist/components/config-generator.js +254 -0
- package/infra/dist/components/hetzner-agent.js +162 -0
- package/infra/dist/components/index.js +18 -0
- package/infra/dist/components/openclaw-agent.js +287 -0
- package/infra/dist/components/shared.js +132 -0
- package/infra/dist/components/types.js +6 -0
- package/infra/dist/index.js +387 -0
- package/infra/dist/shared-vpc.js +167 -0
- package/infra/node_modules/@clawup/core/dist/__tests__/schemas.test.d.ts +2 -0
- package/infra/node_modules/@clawup/core/dist/__tests__/schemas.test.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/__tests__/schemas.test.js +124 -0
- package/infra/node_modules/@clawup/core/dist/__tests__/schemas.test.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/coding-agent-registry.d.ts +32 -0
- package/infra/node_modules/@clawup/core/dist/coding-agent-registry.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/coding-agent-registry.js +56 -0
- package/infra/node_modules/@clawup/core/dist/coding-agent-registry.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/constants.d.ts +137 -0
- package/infra/node_modules/@clawup/core/dist/constants.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/constants.js +314 -0
- package/infra/node_modules/@clawup/core/dist/constants.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/dep-registry.d.ts +25 -0
- package/infra/node_modules/@clawup/core/dist/dep-registry.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/dep-registry.js +46 -0
- package/infra/node_modules/@clawup/core/dist/dep-registry.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/deps.d.ts +18 -0
- package/infra/node_modules/@clawup/core/dist/deps.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/deps.js +39 -0
- package/infra/node_modules/@clawup/core/dist/deps.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/identity.d.ts +20 -0
- package/infra/node_modules/@clawup/core/dist/identity.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/identity.js +217 -0
- package/infra/node_modules/@clawup/core/dist/identity.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/index.d.ts +18 -0
- package/infra/node_modules/@clawup/core/dist/index.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/index.js +52 -0
- package/infra/node_modules/@clawup/core/dist/index.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/plugin-registry.d.ts +13 -0
- package/infra/node_modules/@clawup/core/dist/plugin-registry.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/plugin-registry.js +24 -0
- package/infra/node_modules/@clawup/core/dist/plugin-registry.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/identity.d.ts +74 -0
- package/infra/node_modules/@clawup/core/dist/schemas/identity.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/identity.js +45 -0
- package/infra/node_modules/@clawup/core/dist/schemas/identity.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/index.d.ts +6 -0
- package/infra/node_modules/@clawup/core/dist/schemas/index.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/index.js +13 -0
- package/infra/node_modules/@clawup/core/dist/schemas/index.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/manifest.d.ts +159 -0
- package/infra/node_modules/@clawup/core/dist/schemas/manifest.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/schemas/manifest.js +54 -0
- package/infra/node_modules/@clawup/core/dist/schemas/manifest.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/skills.d.ts +30 -0
- package/infra/node_modules/@clawup/core/dist/skills.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/skills.js +52 -0
- package/infra/node_modules/@clawup/core/dist/skills.js.map +1 -0
- package/infra/node_modules/@clawup/core/dist/types.d.ts +59 -0
- package/infra/node_modules/@clawup/core/dist/types.d.ts.map +1 -0
- package/infra/node_modules/@clawup/core/dist/types.js +30 -0
- package/infra/node_modules/@clawup/core/dist/types.js.map +1 -0
- package/infra/node_modules/@clawup/core/package.json +46 -0
- package/infra/package.json +12 -0
- package/package.json +43 -0
- package/scripts/postinstall.mjs +395 -0
|
@@ -0,0 +1,698 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* clawup init — Interactive setup wizard
|
|
4
|
+
*
|
|
5
|
+
* Identity-driven: every agent must have an identity source.
|
|
6
|
+
* The manifest stores only team composition (which agents to deploy).
|
|
7
|
+
* Plugins, deps, and config come from identities at deploy time.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.initCommand = initCommand;
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const p = __importStar(require("@clack/prompts"));
|
|
46
|
+
const core_1 = require("@clawup/core");
|
|
47
|
+
const identity_1 = require("@clawup/core/identity");
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const prerequisites_1 = require("../lib/prerequisites");
|
|
51
|
+
const pulumi_1 = require("../lib/pulumi");
|
|
52
|
+
const config_1 = require("../lib/config");
|
|
53
|
+
const workspace_1 = require("../lib/workspace");
|
|
54
|
+
const ui_1 = require("../lib/ui");
|
|
55
|
+
async function initCommand(opts = {}) {
|
|
56
|
+
(0, ui_1.showBanner)();
|
|
57
|
+
// -------------------------------------------------------------------------
|
|
58
|
+
// Step 1: Check prerequisites
|
|
59
|
+
// -------------------------------------------------------------------------
|
|
60
|
+
p.log.step("Checking prerequisites...");
|
|
61
|
+
const prereqsOk = await (0, prerequisites_1.checkPrerequisites)();
|
|
62
|
+
if (!prereqsOk) {
|
|
63
|
+
(0, ui_1.exitWithError)("Prerequisites not met. Please install the missing tools and try again.");
|
|
64
|
+
}
|
|
65
|
+
p.log.success("All prerequisites satisfied!");
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
// Step 2: Infrastructure config
|
|
68
|
+
// -------------------------------------------------------------------------
|
|
69
|
+
const stackName = await p.text({
|
|
70
|
+
message: "Pulumi stack name",
|
|
71
|
+
placeholder: "dev",
|
|
72
|
+
defaultValue: "dev",
|
|
73
|
+
});
|
|
74
|
+
(0, ui_1.handleCancel)(stackName);
|
|
75
|
+
const provider = await p.select({
|
|
76
|
+
message: "Cloud provider",
|
|
77
|
+
options: core_1.PROVIDERS.map((prov) => ({ value: prov.value, label: prov.label, hint: prov.hint })),
|
|
78
|
+
initialValue: "aws",
|
|
79
|
+
});
|
|
80
|
+
(0, ui_1.handleCancel)(provider);
|
|
81
|
+
let region;
|
|
82
|
+
let instanceType;
|
|
83
|
+
if (provider === "aws") {
|
|
84
|
+
const awsRegion = await p.select({
|
|
85
|
+
message: "AWS region",
|
|
86
|
+
options: core_1.AWS_REGIONS,
|
|
87
|
+
initialValue: "us-east-1",
|
|
88
|
+
});
|
|
89
|
+
(0, ui_1.handleCancel)(awsRegion);
|
|
90
|
+
region = awsRegion;
|
|
91
|
+
const awsInstanceType = await p.select({
|
|
92
|
+
message: "Default instance type",
|
|
93
|
+
options: core_1.INSTANCE_TYPES,
|
|
94
|
+
initialValue: "t3.medium",
|
|
95
|
+
});
|
|
96
|
+
(0, ui_1.handleCancel)(awsInstanceType);
|
|
97
|
+
instanceType = awsInstanceType;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const hetznerLocation = await p.select({
|
|
101
|
+
message: "Hetzner location",
|
|
102
|
+
options: core_1.HETZNER_LOCATIONS,
|
|
103
|
+
initialValue: "fsn1",
|
|
104
|
+
});
|
|
105
|
+
(0, ui_1.handleCancel)(hetznerLocation);
|
|
106
|
+
region = hetznerLocation;
|
|
107
|
+
const serverTypeOptions = (0, core_1.hetznerServerTypes)(region);
|
|
108
|
+
const hetznerServerType = await p.select({
|
|
109
|
+
message: "Default server type",
|
|
110
|
+
options: serverTypeOptions,
|
|
111
|
+
initialValue: serverTypeOptions[0].value,
|
|
112
|
+
});
|
|
113
|
+
(0, ui_1.handleCancel)(hetznerServerType);
|
|
114
|
+
instanceType = hetznerServerType;
|
|
115
|
+
}
|
|
116
|
+
// -------------------------------------------------------------------------
|
|
117
|
+
// Step 3: Owner info
|
|
118
|
+
// -------------------------------------------------------------------------
|
|
119
|
+
const ownerName = await p.text({
|
|
120
|
+
message: "Owner name (for workspace templates)",
|
|
121
|
+
placeholder: "Boss",
|
|
122
|
+
defaultValue: "Boss",
|
|
123
|
+
});
|
|
124
|
+
(0, ui_1.handleCancel)(ownerName);
|
|
125
|
+
const timezone = await p.text({
|
|
126
|
+
message: "Your timezone",
|
|
127
|
+
placeholder: "PST (America/Los_Angeles)",
|
|
128
|
+
defaultValue: "PST (America/Los_Angeles)",
|
|
129
|
+
});
|
|
130
|
+
(0, ui_1.handleCancel)(timezone);
|
|
131
|
+
const workingHours = await p.text({
|
|
132
|
+
message: "Your working hours",
|
|
133
|
+
placeholder: "9am-6pm",
|
|
134
|
+
defaultValue: "9am-6pm",
|
|
135
|
+
});
|
|
136
|
+
(0, ui_1.handleCancel)(workingHours);
|
|
137
|
+
const userNotes = await p.text({
|
|
138
|
+
message: "Any notes for your agents about you? (optional)",
|
|
139
|
+
placeholder: "e.g., Prefers concise updates, hates unnecessary meetings",
|
|
140
|
+
defaultValue: "",
|
|
141
|
+
});
|
|
142
|
+
(0, ui_1.handleCancel)(userNotes);
|
|
143
|
+
const basicConfig = {
|
|
144
|
+
stackName: stackName,
|
|
145
|
+
provider: provider,
|
|
146
|
+
region,
|
|
147
|
+
instanceType,
|
|
148
|
+
ownerName: ownerName,
|
|
149
|
+
timezone: timezone,
|
|
150
|
+
workingHours: workingHours,
|
|
151
|
+
userNotes: userNotes || "No additional notes provided yet.",
|
|
152
|
+
};
|
|
153
|
+
// -------------------------------------------------------------------------
|
|
154
|
+
// Step 4: Secrets (Anthropic, Tailscale, Hetzner)
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
p.log.step("Configure Anthropic API key");
|
|
157
|
+
p.note(core_1.KEY_INSTRUCTIONS.anthropicApiKey.steps.join("\n"), core_1.KEY_INSTRUCTIONS.anthropicApiKey.title);
|
|
158
|
+
const anthropicApiKey = await p.text({
|
|
159
|
+
message: "Anthropic API key",
|
|
160
|
+
placeholder: `${core_1.MODEL_PROVIDERS.anthropic.keyPrefix}...`,
|
|
161
|
+
validate: (val) => {
|
|
162
|
+
if (!val)
|
|
163
|
+
return "API key is required";
|
|
164
|
+
if (!val.startsWith(core_1.MODEL_PROVIDERS.anthropic.keyPrefix)) {
|
|
165
|
+
return `Must start with ${core_1.MODEL_PROVIDERS.anthropic.keyPrefix}`;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
(0, ui_1.handleCancel)(anthropicApiKey);
|
|
170
|
+
p.log.step("Configure infrastructure secrets");
|
|
171
|
+
p.note(core_1.KEY_INSTRUCTIONS.tailscaleAuthKey.steps.join("\n"), core_1.KEY_INSTRUCTIONS.tailscaleAuthKey.title);
|
|
172
|
+
const tailscaleAuthKey = await p.password({
|
|
173
|
+
message: "Tailscale auth key",
|
|
174
|
+
validate: (val) => {
|
|
175
|
+
if (!val.startsWith("tskey-auth-"))
|
|
176
|
+
return "Must start with tskey-auth-";
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
(0, ui_1.handleCancel)(tailscaleAuthKey);
|
|
180
|
+
p.note(core_1.KEY_INSTRUCTIONS.tailnetDnsName.steps.join("\n"), core_1.KEY_INSTRUCTIONS.tailnetDnsName.title);
|
|
181
|
+
const tailnetDnsName = await p.text({
|
|
182
|
+
message: "Tailnet DNS name",
|
|
183
|
+
placeholder: "my-tailnet.ts.net",
|
|
184
|
+
validate: (val) => {
|
|
185
|
+
if (!val.endsWith(".ts.net"))
|
|
186
|
+
return "Must end with .ts.net";
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
(0, ui_1.handleCancel)(tailnetDnsName);
|
|
190
|
+
p.note(core_1.KEY_INSTRUCTIONS.tailscaleApiKey.steps.join("\n"), core_1.KEY_INSTRUCTIONS.tailscaleApiKey.title);
|
|
191
|
+
const tailscaleApiKey = await p.text({
|
|
192
|
+
message: "Tailscale API key (press Enter to skip)",
|
|
193
|
+
placeholder: "tskey-api-... (optional)",
|
|
194
|
+
defaultValue: "",
|
|
195
|
+
});
|
|
196
|
+
(0, ui_1.handleCancel)(tailscaleApiKey);
|
|
197
|
+
let hcloudToken;
|
|
198
|
+
if (provider === "hetzner") {
|
|
199
|
+
p.note(core_1.KEY_INSTRUCTIONS.hcloudToken.steps.join("\n"), core_1.KEY_INSTRUCTIONS.hcloudToken.title);
|
|
200
|
+
const token = await p.password({
|
|
201
|
+
message: "Hetzner Cloud API token",
|
|
202
|
+
validate: (val) => {
|
|
203
|
+
if (!val)
|
|
204
|
+
return "API token is required for Hetzner deployments";
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
(0, ui_1.handleCancel)(token);
|
|
208
|
+
hcloudToken = token;
|
|
209
|
+
}
|
|
210
|
+
// -------------------------------------------------------------------------
|
|
211
|
+
// Step 5: Agent selection (identity-driven, no custom mode)
|
|
212
|
+
// -------------------------------------------------------------------------
|
|
213
|
+
p.log.step("Configure agents");
|
|
214
|
+
const agentMode = await p.select({
|
|
215
|
+
message: "How would you like to configure agents?",
|
|
216
|
+
options: [
|
|
217
|
+
{ value: "built-in", label: "Built-in agents", hint: "PM (Juno), Eng (Titus), QA (Scout)" },
|
|
218
|
+
{ value: "identity", label: "From identity source", hint: "Load from a Git URL or local path" },
|
|
219
|
+
{ value: "mix", label: "Mix of both", hint: "Pick built-in + add from identity source" },
|
|
220
|
+
],
|
|
221
|
+
});
|
|
222
|
+
(0, ui_1.handleCancel)(agentMode);
|
|
223
|
+
const fetchedIdentities = [];
|
|
224
|
+
const identityCacheDir = path.join(os.homedir(), ".clawup", "identity-cache");
|
|
225
|
+
// Collect built-in agents
|
|
226
|
+
if (agentMode === "built-in" || agentMode === "mix") {
|
|
227
|
+
const selectedBuiltIns = await p.multiselect({
|
|
228
|
+
message: "Select agents",
|
|
229
|
+
options: Object.entries(core_1.BUILT_IN_IDENTITIES).map(([key, entry]) => ({
|
|
230
|
+
value: key,
|
|
231
|
+
label: entry.label,
|
|
232
|
+
hint: entry.hint,
|
|
233
|
+
})),
|
|
234
|
+
required: agentMode === "built-in",
|
|
235
|
+
});
|
|
236
|
+
(0, ui_1.handleCancel)(selectedBuiltIns);
|
|
237
|
+
for (const key of selectedBuiltIns) {
|
|
238
|
+
const entry = core_1.BUILT_IN_IDENTITIES[key];
|
|
239
|
+
const identity = await (0, identity_1.fetchIdentity)(entry.path, identityCacheDir);
|
|
240
|
+
const agent = {
|
|
241
|
+
name: `agent-${identity.manifest.name}`,
|
|
242
|
+
displayName: identity.manifest.displayName,
|
|
243
|
+
role: identity.manifest.role,
|
|
244
|
+
identity: entry.path,
|
|
245
|
+
volumeSize: identity.manifest.volumeSize,
|
|
246
|
+
};
|
|
247
|
+
fetchedIdentities.push({ agent, manifest: identity.manifest });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Collect identity-source agents
|
|
251
|
+
if (agentMode === "identity" || agentMode === "mix") {
|
|
252
|
+
let addMore = true;
|
|
253
|
+
while (addMore) {
|
|
254
|
+
const identityUrl = await p.text({
|
|
255
|
+
message: "Identity source (Git URL or local path)",
|
|
256
|
+
placeholder: "https://github.com/org/identities#agent-name",
|
|
257
|
+
validate: (val) => {
|
|
258
|
+
if (!val.trim())
|
|
259
|
+
return "Identity source is required";
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
(0, ui_1.handleCancel)(identityUrl);
|
|
263
|
+
const spinner = p.spinner();
|
|
264
|
+
spinner.start("Validating identity...");
|
|
265
|
+
try {
|
|
266
|
+
const identity = await (0, identity_1.fetchIdentity)(identityUrl, identityCacheDir);
|
|
267
|
+
spinner.stop(`Found: ${identity.manifest.displayName} (${identity.manifest.role}) — ${identity.manifest.description}`);
|
|
268
|
+
// Allow volume size override
|
|
269
|
+
const volumeOverride = await p.text({
|
|
270
|
+
message: `Volume size in GB (default: ${identity.manifest.volumeSize})`,
|
|
271
|
+
placeholder: String(identity.manifest.volumeSize),
|
|
272
|
+
defaultValue: String(identity.manifest.volumeSize),
|
|
273
|
+
validate: (val) => {
|
|
274
|
+
const n = parseInt(val, 10);
|
|
275
|
+
if (isNaN(n) || n < 8 || n > 500)
|
|
276
|
+
return "Must be between 8 and 500";
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
(0, ui_1.handleCancel)(volumeOverride);
|
|
280
|
+
const agent = {
|
|
281
|
+
name: `agent-${identity.manifest.name}`,
|
|
282
|
+
displayName: identity.manifest.displayName,
|
|
283
|
+
role: identity.manifest.role,
|
|
284
|
+
identity: identityUrl,
|
|
285
|
+
volumeSize: parseInt(volumeOverride, 10),
|
|
286
|
+
};
|
|
287
|
+
fetchedIdentities.push({ agent, manifest: identity.manifest });
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
spinner.stop(`Failed to validate identity: ${err.message}`);
|
|
291
|
+
p.log.error("Please check the URL and try again.");
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
const more = await p.confirm({
|
|
295
|
+
message: "Add another identity-based agent?",
|
|
296
|
+
initialValue: false,
|
|
297
|
+
});
|
|
298
|
+
(0, ui_1.handleCancel)(more);
|
|
299
|
+
addMore = more;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (fetchedIdentities.length === 0) {
|
|
303
|
+
(0, ui_1.exitWithError)("No agents configured. At least one agent is required.");
|
|
304
|
+
}
|
|
305
|
+
const agents = fetchedIdentities.map((fi) => fi.agent);
|
|
306
|
+
// -------------------------------------------------------------------------
|
|
307
|
+
// Step 6: Collect template variable values
|
|
308
|
+
// -------------------------------------------------------------------------
|
|
309
|
+
// Auto-fillable vars from owner info
|
|
310
|
+
const autoVars = {
|
|
311
|
+
OWNER_NAME: basicConfig.ownerName,
|
|
312
|
+
TIMEZONE: basicConfig.timezone,
|
|
313
|
+
WORKING_HOURS: basicConfig.workingHours,
|
|
314
|
+
USER_NOTES: basicConfig.userNotes,
|
|
315
|
+
};
|
|
316
|
+
// Scan all identities for template vars and deduplicate
|
|
317
|
+
const allTemplateVarNames = new Set();
|
|
318
|
+
for (const fi of fetchedIdentities) {
|
|
319
|
+
for (const v of fi.manifest.templateVars ?? []) {
|
|
320
|
+
allTemplateVarNames.add(v);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const templateVars = {};
|
|
324
|
+
// Auto-fill known vars
|
|
325
|
+
for (const varName of allTemplateVarNames) {
|
|
326
|
+
if (autoVars[varName]) {
|
|
327
|
+
templateVars[varName] = autoVars[varName];
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Prompt for remaining vars
|
|
331
|
+
const remainingVars = [...allTemplateVarNames].filter((v) => !templateVars[v]);
|
|
332
|
+
if (remainingVars.length > 0) {
|
|
333
|
+
p.log.step("Configure template variables");
|
|
334
|
+
p.log.info(`Your agents use the following template variables: ${remainingVars.join(", ")}`);
|
|
335
|
+
for (const varName of remainingVars) {
|
|
336
|
+
const value = await p.text({
|
|
337
|
+
message: `Value for ${varName}`,
|
|
338
|
+
placeholder: varName === "LINEAR_TEAM" ? "e.g., ENG" : varName === "GITHUB_REPO" ? "https://github.com/org/repo" : "",
|
|
339
|
+
validate: (val) => {
|
|
340
|
+
if (!val.trim())
|
|
341
|
+
return `${varName} is required`;
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
(0, ui_1.handleCancel)(value);
|
|
345
|
+
templateVars[varName] = value;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// -------------------------------------------------------------------------
|
|
349
|
+
// Step 7: Collect integration credentials (driven by identity plugins/deps)
|
|
350
|
+
// -------------------------------------------------------------------------
|
|
351
|
+
p.log.step("Configure integrations");
|
|
352
|
+
// Determine which plugins and deps are needed across all identities
|
|
353
|
+
const agentPlugins = new Map(); // agent name → plugin names
|
|
354
|
+
const agentDeps = new Map(); // agent name → dep names
|
|
355
|
+
const allPluginNames = new Set();
|
|
356
|
+
const allDepNames = new Set();
|
|
357
|
+
for (const fi of fetchedIdentities) {
|
|
358
|
+
const plugins = new Set(fi.manifest.plugins ?? []);
|
|
359
|
+
const deps = new Set(fi.manifest.deps ?? []);
|
|
360
|
+
agentPlugins.set(fi.agent.name, plugins);
|
|
361
|
+
agentDeps.set(fi.agent.name, deps);
|
|
362
|
+
for (const pl of plugins)
|
|
363
|
+
allPluginNames.add(pl);
|
|
364
|
+
for (const d of deps)
|
|
365
|
+
allDepNames.add(d);
|
|
366
|
+
}
|
|
367
|
+
// Track identity pluginDefaults per agent name for seeding plugin config files
|
|
368
|
+
const identityPluginDefaults = {};
|
|
369
|
+
for (const fi of fetchedIdentities) {
|
|
370
|
+
if (fi.manifest.pluginDefaults) {
|
|
371
|
+
identityPluginDefaults[fi.agent.name] = fi.manifest.pluginDefaults;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Per-agent integration credentials
|
|
375
|
+
const integrationCredentials = {};
|
|
376
|
+
for (const agent of agents) {
|
|
377
|
+
integrationCredentials[agent.role] = {};
|
|
378
|
+
}
|
|
379
|
+
// Slack credentials — only if any identity has the slack plugin
|
|
380
|
+
const slackCredentials = {};
|
|
381
|
+
if (allPluginNames.has("slack")) {
|
|
382
|
+
p.note(core_1.KEY_INSTRUCTIONS.slackCredentials.steps.join("\n"), core_1.KEY_INSTRUCTIONS.slackCredentials.title);
|
|
383
|
+
for (const fi of fetchedIdentities) {
|
|
384
|
+
if (!agentPlugins.get(fi.agent.name)?.has("slack"))
|
|
385
|
+
continue;
|
|
386
|
+
const slackManifest = (0, core_1.slackAppManifest)(fi.agent.displayName);
|
|
387
|
+
try {
|
|
388
|
+
(0, child_process_1.execSync)(process.platform === "darwin" ? "pbcopy" : "xclip -selection clipboard", { input: slackManifest });
|
|
389
|
+
p.log.success(`Slack manifest for ${fi.agent.displayName} copied to clipboard — paste it into Slack`);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
p.log.warn(`Could not copy to clipboard. Manifest for ${fi.agent.displayName}:`);
|
|
393
|
+
console.log(slackManifest);
|
|
394
|
+
}
|
|
395
|
+
const botToken = await p.password({
|
|
396
|
+
message: `Slack Bot Token for ${fi.agent.displayName} (${fi.agent.role})`,
|
|
397
|
+
validate: (val) => {
|
|
398
|
+
if (!val.startsWith("xoxb-"))
|
|
399
|
+
return "Must start with xoxb-";
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
(0, ui_1.handleCancel)(botToken);
|
|
403
|
+
const appToken = await p.password({
|
|
404
|
+
message: `Slack App Token for ${fi.agent.displayName} (${fi.agent.role})`,
|
|
405
|
+
validate: (val) => {
|
|
406
|
+
if (!val.startsWith("xapp-"))
|
|
407
|
+
return "Must start with xapp-";
|
|
408
|
+
},
|
|
409
|
+
});
|
|
410
|
+
(0, ui_1.handleCancel)(appToken);
|
|
411
|
+
slackCredentials[fi.agent.role] = {
|
|
412
|
+
botToken: botToken,
|
|
413
|
+
appToken: appToken,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
// Linear credentials — only if any identity has the openclaw-linear plugin
|
|
418
|
+
if (allPluginNames.has("openclaw-linear")) {
|
|
419
|
+
p.note(core_1.KEY_INSTRUCTIONS.linearApiKey.steps.join("\n"), core_1.KEY_INSTRUCTIONS.linearApiKey.title);
|
|
420
|
+
const linearAgents = fetchedIdentities.filter((fi) => agentPlugins.get(fi.agent.name)?.has("openclaw-linear"));
|
|
421
|
+
for (const fi of linearAgents) {
|
|
422
|
+
const linearKey = await p.password({
|
|
423
|
+
message: `Linear API key for ${fi.agent.displayName} (${fi.agent.role})`,
|
|
424
|
+
validate: (val) => {
|
|
425
|
+
if (!val.startsWith("lin_api_"))
|
|
426
|
+
return "Must start with lin_api_";
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
(0, ui_1.handleCancel)(linearKey);
|
|
430
|
+
integrationCredentials[fi.agent.role].linearApiKey = linearKey;
|
|
431
|
+
// Auto-fetch user UUID from Linear API
|
|
432
|
+
const s = p.spinner();
|
|
433
|
+
s.start(`Fetching Linear user ID for ${fi.agent.displayName}...`);
|
|
434
|
+
try {
|
|
435
|
+
const res = await fetch("https://api.linear.app/graphql", {
|
|
436
|
+
method: "POST",
|
|
437
|
+
headers: {
|
|
438
|
+
"Content-Type": "application/json",
|
|
439
|
+
Authorization: linearKey,
|
|
440
|
+
},
|
|
441
|
+
body: JSON.stringify({ query: "{ viewer { id } }" }),
|
|
442
|
+
});
|
|
443
|
+
const data = (await res.json());
|
|
444
|
+
const uuid = data?.data?.viewer?.id;
|
|
445
|
+
if (!uuid)
|
|
446
|
+
throw new Error("No user ID in response");
|
|
447
|
+
integrationCredentials[fi.agent.role].linearUserUuid = uuid;
|
|
448
|
+
s.stop(`${fi.agent.displayName}: ${uuid}`);
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
s.stop(`Could not fetch Linear user ID for ${fi.agent.displayName}`);
|
|
452
|
+
p.log.warn(`${err instanceof Error ? err.message : String(err)}`);
|
|
453
|
+
const linearUserUuid = await p.text({
|
|
454
|
+
message: `Enter Linear user UUID manually for ${fi.agent.displayName}`,
|
|
455
|
+
placeholder: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
|
456
|
+
validate: (val) => {
|
|
457
|
+
if (!val)
|
|
458
|
+
return "Linear user UUID is required";
|
|
459
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(val)) {
|
|
460
|
+
return "Must be a valid UUID format";
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
(0, ui_1.handleCancel)(linearUserUuid);
|
|
465
|
+
integrationCredentials[fi.agent.role].linearUserUuid = linearUserUuid;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Linear webhook signing secrets
|
|
469
|
+
p.note([
|
|
470
|
+
"Create a webhook in Linear for each agent:",
|
|
471
|
+
"1. Go to Settings → API → Webhooks → \"New webhook\"",
|
|
472
|
+
"2. Paste the webhook URL shown below for each agent",
|
|
473
|
+
"3. Select events to receive (e.g., Issues, Comments)",
|
|
474
|
+
"4. Copy the \"Signing secret\" shown after creating the webhook",
|
|
475
|
+
].join("\n"), "Linear Webhook Setup");
|
|
476
|
+
for (const fi of linearAgents) {
|
|
477
|
+
const webhookUrl = `https://${(0, core_1.tailscaleHostname)(basicConfig.stackName, fi.agent.name)}.${tailnetDnsName}/hooks/linear`;
|
|
478
|
+
p.log.info(`${fi.agent.displayName} (${fi.agent.role}): ${webhookUrl}`);
|
|
479
|
+
const webhookSecretInput = await p.password({
|
|
480
|
+
message: `Signing secret for ${fi.agent.displayName} (${fi.agent.role})`,
|
|
481
|
+
validate: (val) => {
|
|
482
|
+
if (!val)
|
|
483
|
+
return "Webhook signing secret is required";
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
(0, ui_1.handleCancel)(webhookSecretInput);
|
|
487
|
+
integrationCredentials[fi.agent.role].linearWebhookSecret = webhookSecretInput;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// GitHub token — only if any identity has the gh dep
|
|
491
|
+
if (allDepNames.has("gh")) {
|
|
492
|
+
p.note(core_1.KEY_INSTRUCTIONS.githubToken.steps.join("\n"), core_1.KEY_INSTRUCTIONS.githubToken.title);
|
|
493
|
+
for (const fi of fetchedIdentities) {
|
|
494
|
+
if (!agentDeps.get(fi.agent.name)?.has("gh"))
|
|
495
|
+
continue;
|
|
496
|
+
const githubKey = await p.password({
|
|
497
|
+
message: `GitHub token for ${fi.agent.displayName} (${fi.agent.role})`,
|
|
498
|
+
validate: (val) => {
|
|
499
|
+
if (!val.startsWith("ghp_") && !val.startsWith("github_pat_")) {
|
|
500
|
+
return "Must start with ghp_ or github_pat_";
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
(0, ui_1.handleCancel)(githubKey);
|
|
505
|
+
integrationCredentials[fi.agent.role].githubToken = githubKey;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
// Brave Search API key — only if any identity has the brave-search dep (global, once)
|
|
509
|
+
let braveApiKey;
|
|
510
|
+
if (allDepNames.has("brave-search")) {
|
|
511
|
+
p.note(core_1.KEY_INSTRUCTIONS.braveApiKey.steps.join("\n"), core_1.KEY_INSTRUCTIONS.braveApiKey.title);
|
|
512
|
+
const braveKey = await p.password({
|
|
513
|
+
message: "Brave Search API key",
|
|
514
|
+
validate: (val) => {
|
|
515
|
+
if (!val)
|
|
516
|
+
return "API key is required";
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
(0, ui_1.handleCancel)(braveKey);
|
|
520
|
+
braveApiKey = braveKey;
|
|
521
|
+
}
|
|
522
|
+
// -------------------------------------------------------------------------
|
|
523
|
+
// Step 8: Summary
|
|
524
|
+
// -------------------------------------------------------------------------
|
|
525
|
+
const costEstimates = basicConfig.provider === "aws" ? core_1.COST_ESTIMATES : core_1.HETZNER_COST_ESTIMATES;
|
|
526
|
+
const costPerAgent = costEstimates[basicConfig.instanceType] ?? 30;
|
|
527
|
+
const totalCost = agents.reduce((sum, a) => {
|
|
528
|
+
const agentCost = costEstimates[a.instanceType ?? basicConfig.instanceType] ?? costPerAgent;
|
|
529
|
+
return sum + agentCost;
|
|
530
|
+
}, 0);
|
|
531
|
+
const integrationNames = [];
|
|
532
|
+
if (allPluginNames.has("openclaw-linear"))
|
|
533
|
+
integrationNames.push("Linear");
|
|
534
|
+
if (allPluginNames.has("slack"))
|
|
535
|
+
integrationNames.push("Slack");
|
|
536
|
+
if (allDepNames.has("gh"))
|
|
537
|
+
integrationNames.push("GitHub CLI");
|
|
538
|
+
if (allDepNames.has("brave-search"))
|
|
539
|
+
integrationNames.push("Brave Search");
|
|
540
|
+
const providerLabel = basicConfig.provider === "aws" ? "AWS" : "Hetzner";
|
|
541
|
+
const regionLabel = basicConfig.provider === "aws" ? "Region" : "Location";
|
|
542
|
+
// Build template vars display (excluding auto-filled owner vars)
|
|
543
|
+
const customVarEntries = Object.entries(templateVars).filter(([k]) => !autoVars[k]);
|
|
544
|
+
const summaryLines = [
|
|
545
|
+
`Stack: ${basicConfig.stackName}`,
|
|
546
|
+
`Provider: ${providerLabel}`,
|
|
547
|
+
`${regionLabel.padEnd(14, " ")} ${basicConfig.region}`,
|
|
548
|
+
`Instance type: ${basicConfig.instanceType}`,
|
|
549
|
+
`Owner: ${basicConfig.ownerName}`,
|
|
550
|
+
`Timezone: ${basicConfig.timezone}`,
|
|
551
|
+
`Working hours: ${basicConfig.workingHours}`,
|
|
552
|
+
];
|
|
553
|
+
if (customVarEntries.length > 0) {
|
|
554
|
+
for (const [k, v] of customVarEntries) {
|
|
555
|
+
summaryLines.push(`${k.padEnd(14, " ")} ${v}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (integrationNames.length > 0) {
|
|
559
|
+
summaryLines.push(`Integrations: ${integrationNames.join(", ")}`);
|
|
560
|
+
}
|
|
561
|
+
summaryLines.push(``, `Agents (${agents.length}):`, (0, ui_1.formatAgentList)(agents), ``, `Estimated cost: ${(0, ui_1.formatCost)(totalCost)}`);
|
|
562
|
+
p.note(summaryLines.join("\n"), "Deployment Summary");
|
|
563
|
+
// -------------------------------------------------------------------------
|
|
564
|
+
// Step 9: Confirm
|
|
565
|
+
// -------------------------------------------------------------------------
|
|
566
|
+
const confirmed = await p.confirm({
|
|
567
|
+
message: "Proceed with setup?",
|
|
568
|
+
});
|
|
569
|
+
(0, ui_1.handleCancel)(confirmed);
|
|
570
|
+
if (!confirmed) {
|
|
571
|
+
p.cancel("Setup cancelled.");
|
|
572
|
+
process.exit(0);
|
|
573
|
+
}
|
|
574
|
+
// -------------------------------------------------------------------------
|
|
575
|
+
// Step 10: Execute setup
|
|
576
|
+
// -------------------------------------------------------------------------
|
|
577
|
+
const s = p.spinner();
|
|
578
|
+
// Set up workspace
|
|
579
|
+
s.start("Setting up workspace...");
|
|
580
|
+
const wsResult = (0, workspace_1.ensureWorkspace)();
|
|
581
|
+
if (!wsResult.ok) {
|
|
582
|
+
s.stop("Failed to set up workspace");
|
|
583
|
+
(0, ui_1.exitWithError)(wsResult.error ?? "Failed to set up workspace.");
|
|
584
|
+
}
|
|
585
|
+
s.stop("Workspace ready");
|
|
586
|
+
const cwd = (0, workspace_1.getWorkspaceDir)();
|
|
587
|
+
// Select/create stack
|
|
588
|
+
s.start("Selecting Pulumi stack...");
|
|
589
|
+
const stackResult = (0, pulumi_1.selectOrCreateStack)(basicConfig.stackName, cwd);
|
|
590
|
+
if (!stackResult.ok) {
|
|
591
|
+
s.stop("Failed to select/create stack");
|
|
592
|
+
if (stackResult.error)
|
|
593
|
+
p.log.error(stackResult.error);
|
|
594
|
+
(0, ui_1.exitWithError)(`Could not select or create Pulumi stack "${basicConfig.stackName}".`);
|
|
595
|
+
}
|
|
596
|
+
s.stop("Pulumi stack ready");
|
|
597
|
+
// Set Pulumi config
|
|
598
|
+
s.start("Setting Pulumi configuration...");
|
|
599
|
+
(0, pulumi_1.setConfig)("provider", basicConfig.provider, false, cwd);
|
|
600
|
+
if (basicConfig.provider === "aws") {
|
|
601
|
+
(0, pulumi_1.setConfig)("aws:region", basicConfig.region, false, cwd);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
(0, pulumi_1.setConfig)("hetzner:location", basicConfig.region, false, cwd);
|
|
605
|
+
if (hcloudToken) {
|
|
606
|
+
(0, pulumi_1.setConfig)("hcloud:token", hcloudToken, true, cwd);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
(0, pulumi_1.setConfig)("anthropicApiKey", anthropicApiKey, true, cwd);
|
|
610
|
+
(0, pulumi_1.setConfig)("tailscaleAuthKey", tailscaleAuthKey, true, cwd);
|
|
611
|
+
(0, pulumi_1.setConfig)("tailnetDnsName", tailnetDnsName, false, cwd);
|
|
612
|
+
if (tailscaleApiKey) {
|
|
613
|
+
(0, pulumi_1.setConfig)("tailscaleApiKey", tailscaleApiKey, true, cwd);
|
|
614
|
+
}
|
|
615
|
+
(0, pulumi_1.setConfig)("instanceType", basicConfig.instanceType, false, cwd);
|
|
616
|
+
(0, pulumi_1.setConfig)("ownerName", basicConfig.ownerName, false, cwd);
|
|
617
|
+
(0, pulumi_1.setConfig)("timezone", basicConfig.timezone, false, cwd);
|
|
618
|
+
(0, pulumi_1.setConfig)("workingHours", basicConfig.workingHours, false, cwd);
|
|
619
|
+
(0, pulumi_1.setConfig)("userNotes", basicConfig.userNotes, false, cwd);
|
|
620
|
+
// Set per-agent integration credentials
|
|
621
|
+
for (const [role, creds] of Object.entries(integrationCredentials)) {
|
|
622
|
+
if (creds.linearApiKey)
|
|
623
|
+
(0, pulumi_1.setConfig)(`${role}LinearApiKey`, creds.linearApiKey, true, cwd);
|
|
624
|
+
if (creds.linearWebhookSecret)
|
|
625
|
+
(0, pulumi_1.setConfig)(`${role}LinearWebhookSecret`, creds.linearWebhookSecret, true, cwd);
|
|
626
|
+
if (creds.linearUserUuid)
|
|
627
|
+
(0, pulumi_1.setConfig)(`${role}LinearUserUuid`, creds.linearUserUuid, false, cwd);
|
|
628
|
+
if (creds.githubToken)
|
|
629
|
+
(0, pulumi_1.setConfig)(`${role}GithubToken`, creds.githubToken, true, cwd);
|
|
630
|
+
}
|
|
631
|
+
// Set per-agent Slack credentials
|
|
632
|
+
for (const [role, creds] of Object.entries(slackCredentials)) {
|
|
633
|
+
(0, pulumi_1.setConfig)(`${role}SlackBotToken`, creds.botToken, true, cwd);
|
|
634
|
+
(0, pulumi_1.setConfig)(`${role}SlackAppToken`, creds.appToken, true, cwd);
|
|
635
|
+
}
|
|
636
|
+
if (braveApiKey)
|
|
637
|
+
(0, pulumi_1.setConfig)("braveApiKey", braveApiKey, true, cwd);
|
|
638
|
+
s.stop("Configuration saved");
|
|
639
|
+
// Write manifest
|
|
640
|
+
const configName = basicConfig.stackName;
|
|
641
|
+
s.start(`Writing config to ~/.clawup/configs/${configName}.yaml...`);
|
|
642
|
+
// Only include non-auto template vars in manifest (owner vars are derived at deploy time)
|
|
643
|
+
const manifestTemplateVars = {};
|
|
644
|
+
for (const [k, v] of Object.entries(templateVars)) {
|
|
645
|
+
if (!autoVars[k]) {
|
|
646
|
+
manifestTemplateVars[k] = v;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const manifest = {
|
|
650
|
+
stackName: configName,
|
|
651
|
+
provider: basicConfig.provider,
|
|
652
|
+
region: basicConfig.region,
|
|
653
|
+
instanceType: basicConfig.instanceType,
|
|
654
|
+
ownerName: basicConfig.ownerName,
|
|
655
|
+
timezone: basicConfig.timezone,
|
|
656
|
+
workingHours: basicConfig.workingHours,
|
|
657
|
+
userNotes: basicConfig.userNotes,
|
|
658
|
+
templateVars: Object.keys(manifestTemplateVars).length > 0 ? manifestTemplateVars : undefined,
|
|
659
|
+
agents,
|
|
660
|
+
};
|
|
661
|
+
// Inline plugin config into each agent definition
|
|
662
|
+
for (const fi of fetchedIdentities) {
|
|
663
|
+
const rolePlugins = agentPlugins.get(fi.agent.name);
|
|
664
|
+
if (!rolePlugins || rolePlugins.size === 0)
|
|
665
|
+
continue;
|
|
666
|
+
const inlinePlugins = {};
|
|
667
|
+
const defaults = identityPluginDefaults[fi.agent.name] ?? {};
|
|
668
|
+
for (const pluginName of rolePlugins) {
|
|
669
|
+
const pluginDefaults = defaults[pluginName] ?? {};
|
|
670
|
+
const agentConfig = {
|
|
671
|
+
...pluginDefaults,
|
|
672
|
+
agentId: fi.agent.name,
|
|
673
|
+
};
|
|
674
|
+
// Layer on user-provided config
|
|
675
|
+
if (pluginName === "openclaw-linear") {
|
|
676
|
+
const creds = integrationCredentials[fi.agent.role];
|
|
677
|
+
if (creds?.linearUserUuid) {
|
|
678
|
+
agentConfig.linearUserUuid = creds.linearUserUuid;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
inlinePlugins[pluginName] = agentConfig;
|
|
682
|
+
}
|
|
683
|
+
if (Object.keys(inlinePlugins).length > 0) {
|
|
684
|
+
fi.agent.plugins = inlinePlugins;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
(0, config_1.saveManifest)(configName, manifest);
|
|
688
|
+
s.stop("Config saved");
|
|
689
|
+
if (opts.deploy) {
|
|
690
|
+
p.log.success("Config saved! Starting deployment...\n");
|
|
691
|
+
const { deployCommand } = await Promise.resolve().then(() => __importStar(require("./deploy.js")));
|
|
692
|
+
await deployCommand({ config: configName, yes: opts.yes });
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
p.outro("Setup complete! Run `clawup deploy` to deploy your agents.");
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
//# sourceMappingURL=init.js.map
|