@polpo-ai/cli 0.6.4 → 0.6.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/commands/cloud/byok.js +44 -22
- package/dist/commands/cloud/byok.js.map +1 -1
- package/dist/commands/cloud/deploy.js +254 -134
- package/dist/commands/cloud/deploy.js.map +1 -1
- package/dist/commands/cloud/login.js +42 -20
- package/dist/commands/cloud/login.js.map +1 -1
- package/dist/commands/cloud/logout.js +8 -1
- package/dist/commands/cloud/logout.js.map +1 -1
- package/dist/commands/cloud/projects.js +24 -10
- package/dist/commands/cloud/projects.js.map +1 -1
- package/dist/commands/cloud/status.js +9 -8
- package/dist/commands/cloud/status.js.map +1 -1
- package/dist/commands/create.js +69 -15
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/link.js +167 -9
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/orgs.js +12 -6
- package/dist/commands/orgs.js.map +1 -1
- package/dist/commands/update.js +24 -17
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/whoami.js +18 -11
- package/dist/commands/whoami.js.map +1 -1
- package/dist/util/conflicts.js +90 -0
- package/dist/util/conflicts.js.map +1 -0
- package/dist/util/pull.js +286 -0
- package/dist/util/pull.js.map +1 -0
- package/package.json +3 -3
|
@@ -18,14 +18,16 @@
|
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import pc from "picocolors";
|
|
21
|
+
import * as clack from "@clack/prompts";
|
|
21
22
|
import { createApiClient } from "./api.js";
|
|
22
|
-
import { isTTY, confirm } from "./prompt.js";
|
|
23
23
|
import { resolveKey, decrypt } from "@polpo-ai/vault-crypto";
|
|
24
24
|
import { AddAgentSchema } from "@polpo-ai/server";
|
|
25
25
|
import { friendlyError } from "../../util/errors.js";
|
|
26
26
|
import { pickOrg } from "../../util/org.js";
|
|
27
27
|
import { resolveOrCreateProject } from "../../util/project.js";
|
|
28
28
|
import { requireAuth } from "../../util/auth.js";
|
|
29
|
+
import { isTTY } from "./prompt.js";
|
|
30
|
+
import { resolveDeployConflict } from "../../util/conflicts.js";
|
|
29
31
|
function emptyResult() {
|
|
30
32
|
return { created: 0, updated: 0, skipped: 0, failed: 0, errors: [] };
|
|
31
33
|
}
|
|
@@ -70,33 +72,64 @@ function listJsonFiles(dir) {
|
|
|
70
72
|
return fs.readdirSync(dir).filter(f => f.endsWith(".json")).map(f => path.join(dir, f));
|
|
71
73
|
}
|
|
72
74
|
// ── Core deployers ──────────────────────────────────────
|
|
73
|
-
async function deployTeams(client, polpoDir) {
|
|
75
|
+
async function deployTeams(client, polpoDir, opts) {
|
|
74
76
|
const result = emptyResult();
|
|
75
77
|
const teams = loadJson(path.join(polpoDir, "teams.json"));
|
|
76
78
|
if (!teams || !Array.isArray(teams))
|
|
77
79
|
return result;
|
|
80
|
+
// Fetch existing teams for conflict detection
|
|
81
|
+
let existingTeams = {};
|
|
82
|
+
try {
|
|
83
|
+
const res = await client.get("/v1/agents/teams");
|
|
84
|
+
if (res.status === 200) {
|
|
85
|
+
const data = res.data?.data ?? res.data ?? [];
|
|
86
|
+
if (Array.isArray(data)) {
|
|
87
|
+
for (const t of data)
|
|
88
|
+
existingTeams[t.name] = t;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch { /* proceed without comparison */ }
|
|
78
93
|
for (const team of teams) {
|
|
79
94
|
if (!team.name || typeof team.name !== "string") {
|
|
80
95
|
result.errors.push(`team missing "name" field`);
|
|
81
96
|
result.failed++;
|
|
82
97
|
continue;
|
|
83
98
|
}
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
else if (res.status === 409 || res.data?.error?.includes("already exists")) {
|
|
99
|
+
const remote = existingTeams[team.name];
|
|
100
|
+
const local = { name: team.name, description: team.description };
|
|
101
|
+
const action = await resolveDeployConflict(local, remote, `team "${team.name}"`, opts);
|
|
102
|
+
if (action === "skip") {
|
|
89
103
|
result.skipped++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (remote) {
|
|
107
|
+
// Update existing
|
|
108
|
+
const res = await client.put(`/v1/agents/team`, { name: team.name, description: team.description });
|
|
109
|
+
if (res.status >= 200 && res.status < 300) {
|
|
110
|
+
result.updated++;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const msg = res.data?.error ?? `HTTP ${res.status}`;
|
|
114
|
+
result.errors.push(`team "${team.name}": ${friendlyError(msg)}`);
|
|
115
|
+
result.failed++;
|
|
116
|
+
}
|
|
90
117
|
}
|
|
91
118
|
else {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
const res = await client.post("/v1/agents/teams", local);
|
|
120
|
+
if (res.status >= 200 && res.status < 300) {
|
|
121
|
+
result.created++;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const msg = res.data?.error ?? `HTTP ${res.status}`;
|
|
125
|
+
result.errors.push(`team "${team.name}": ${friendlyError(msg)}`);
|
|
126
|
+
result.failed++;
|
|
127
|
+
}
|
|
95
128
|
}
|
|
96
129
|
}
|
|
97
130
|
return result;
|
|
98
131
|
}
|
|
99
|
-
async function deployAgents(client, polpoDir,
|
|
132
|
+
async function deployAgents(client, polpoDir, opts) {
|
|
100
133
|
const result = emptyResult();
|
|
101
134
|
const raw = loadJson(path.join(polpoDir, "agents.json"));
|
|
102
135
|
if (!raw || !Array.isArray(raw)) {
|
|
@@ -106,21 +139,22 @@ async function deployAgents(client, polpoDir, force) {
|
|
|
106
139
|
}
|
|
107
140
|
return result;
|
|
108
141
|
}
|
|
109
|
-
// Fetch existing agents for
|
|
110
|
-
let
|
|
142
|
+
// Fetch existing agents for conflict detection
|
|
143
|
+
let existingAgents = {};
|
|
111
144
|
try {
|
|
112
145
|
const res = await client.get("/v1/agents");
|
|
113
146
|
if (res.status === 200) {
|
|
114
147
|
const data = res.data?.data ?? res.data ?? [];
|
|
115
|
-
if (Array.isArray(data))
|
|
116
|
-
|
|
148
|
+
if (Array.isArray(data)) {
|
|
149
|
+
for (const a of data)
|
|
150
|
+
existingAgents[a.name] = a;
|
|
151
|
+
}
|
|
117
152
|
}
|
|
118
153
|
}
|
|
119
|
-
catch { /*
|
|
154
|
+
catch { /* proceed without comparison */ }
|
|
120
155
|
for (const entry of raw) {
|
|
121
156
|
const agent = entry.agent ?? entry;
|
|
122
157
|
const teamName = entry.teamName ?? "default";
|
|
123
|
-
// Validate agent schema
|
|
124
158
|
const parsed = AddAgentSchema.safeParse(agent);
|
|
125
159
|
if (!parsed.success) {
|
|
126
160
|
const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
@@ -128,15 +162,13 @@ async function deployAgents(client, polpoDir, force) {
|
|
|
128
162
|
result.failed++;
|
|
129
163
|
continue;
|
|
130
164
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
}
|
|
165
|
+
const remote = existingAgents[agent.name];
|
|
166
|
+
const action = await resolveDeployConflict(agent, remote, `agent "${agent.name}"`, opts);
|
|
167
|
+
if (action === "skip") {
|
|
168
|
+
result.skipped++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (remote) {
|
|
140
172
|
const res = await client.put(`/v1/agents/${encodeURIComponent(agent.name)}`, { ...agent, team: teamName });
|
|
141
173
|
if (res.status >= 200 && res.status < 300) {
|
|
142
174
|
result.updated++;
|
|
@@ -161,17 +193,31 @@ async function deployAgents(client, polpoDir, force) {
|
|
|
161
193
|
}
|
|
162
194
|
return result;
|
|
163
195
|
}
|
|
164
|
-
async function deployMemory(client, polpoDir) {
|
|
196
|
+
async function deployMemory(client, polpoDir, opts) {
|
|
165
197
|
const result = emptyResult();
|
|
166
198
|
const shared = loadText(path.join(polpoDir, "memory.md"));
|
|
167
199
|
if (shared) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
200
|
+
// Fetch existing shared memory for comparison
|
|
201
|
+
let remoteShared = null;
|
|
202
|
+
try {
|
|
203
|
+
const r = await client.get("/v1/memory");
|
|
204
|
+
if (r.status === 200)
|
|
205
|
+
remoteShared = r.data?.content ?? null;
|
|
206
|
+
}
|
|
207
|
+
catch { }
|
|
208
|
+
const action = await resolveDeployConflict(shared, remoteShared, "shared memory", opts);
|
|
209
|
+
if (action === "write") {
|
|
210
|
+
const res = await client.put("/v1/memory", { content: shared });
|
|
211
|
+
if (res.status >= 200 && res.status < 300) {
|
|
212
|
+
result.updated++;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
result.errors.push(`memory: ${friendlyError(res.data?.error ?? `HTTP ${res.status}`)}`);
|
|
216
|
+
result.failed++;
|
|
217
|
+
}
|
|
171
218
|
}
|
|
172
219
|
else {
|
|
173
|
-
result.
|
|
174
|
-
result.failed++;
|
|
220
|
+
result.skipped++;
|
|
175
221
|
}
|
|
176
222
|
}
|
|
177
223
|
const memDir = path.join(polpoDir, "memory");
|
|
@@ -180,13 +226,26 @@ async function deployMemory(client, polpoDir) {
|
|
|
180
226
|
const agentName = file.replace(".md", "");
|
|
181
227
|
const content = loadText(path.join(memDir, file));
|
|
182
228
|
if (content) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
229
|
+
let remoteAgent = null;
|
|
230
|
+
try {
|
|
231
|
+
const r = await client.get(`/v1/memory/agent/${agentName}`);
|
|
232
|
+
if (r.status === 200)
|
|
233
|
+
remoteAgent = r.data?.content ?? null;
|
|
234
|
+
}
|
|
235
|
+
catch { }
|
|
236
|
+
const action = await resolveDeployConflict(content, remoteAgent, `memory "${agentName}"`, opts);
|
|
237
|
+
if (action === "write") {
|
|
238
|
+
const res = await client.put(`/v1/memory/agent/${agentName}`, { content });
|
|
239
|
+
if (res.status >= 200 && res.status < 300) {
|
|
240
|
+
result.updated++;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
result.errors.push(`memory "${agentName}": ${friendlyError(res.data?.error ?? `HTTP ${res.status}`)}`);
|
|
244
|
+
result.failed++;
|
|
245
|
+
}
|
|
186
246
|
}
|
|
187
247
|
else {
|
|
188
|
-
result.
|
|
189
|
-
result.failed++;
|
|
248
|
+
result.skipped++;
|
|
190
249
|
}
|
|
191
250
|
}
|
|
192
251
|
}
|
|
@@ -249,11 +308,24 @@ async function deployPlaybooks(client, polpoDir) {
|
|
|
249
308
|
}
|
|
250
309
|
return result;
|
|
251
310
|
}
|
|
252
|
-
async function deploySkills(client, polpoDir,
|
|
311
|
+
async function deploySkills(client, polpoDir, opts) {
|
|
253
312
|
const result = emptyResult();
|
|
254
313
|
const skillsDir = path.join(polpoDir, "skills");
|
|
255
314
|
if (!fs.existsSync(skillsDir))
|
|
256
315
|
return result;
|
|
316
|
+
// Fetch existing skills for conflict detection
|
|
317
|
+
let existingSkills = {};
|
|
318
|
+
try {
|
|
319
|
+
const res = await client.get("/v1/skills");
|
|
320
|
+
if (res.status === 200) {
|
|
321
|
+
const data = res.data?.data ?? res.data ?? [];
|
|
322
|
+
if (Array.isArray(data)) {
|
|
323
|
+
for (const s of data)
|
|
324
|
+
existingSkills[s.name] = s;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch { }
|
|
257
329
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
258
330
|
if (!entry.isDirectory())
|
|
259
331
|
continue;
|
|
@@ -292,36 +364,40 @@ async function deploySkills(client, polpoDir, force) {
|
|
|
292
364
|
}
|
|
293
365
|
const bodyMatch = raw.match(/^---\n[\s\S]*?\n---\n?([\s\S]*)$/);
|
|
294
366
|
const content = bodyMatch ? bodyMatch[1].trim() : raw.trim();
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
result.created++;
|
|
367
|
+
const localSkill = { name, description, content };
|
|
368
|
+
const remote = existingSkills[name];
|
|
369
|
+
const action = await resolveDeployConflict(localSkill, remote ? { name: remote.name, description: remote.description, content: remote.content } : null, `skill "${name}"`, opts);
|
|
370
|
+
if (action === "skip") {
|
|
371
|
+
result.skipped++;
|
|
372
|
+
continue;
|
|
302
373
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (updateRes.status >= 200 && updateRes.status < 300) {
|
|
311
|
-
result.updated++;
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
result.skipped++;
|
|
315
|
-
}
|
|
374
|
+
if (remote) {
|
|
375
|
+
const updateRes = await client.put(`/v1/skills/${encodeURIComponent(name)}`, {
|
|
376
|
+
description, content,
|
|
377
|
+
...(allowedTools?.length ? { allowedTools } : {}),
|
|
378
|
+
});
|
|
379
|
+
if (updateRes.status >= 200 && updateRes.status < 300) {
|
|
380
|
+
result.updated++;
|
|
316
381
|
}
|
|
317
382
|
else {
|
|
318
|
-
|
|
383
|
+
const msg = updateRes.data?.error ?? `HTTP ${updateRes.status}`;
|
|
384
|
+
result.errors.push(`skill "${name}": ${friendlyError(msg)}`);
|
|
385
|
+
result.failed++;
|
|
319
386
|
}
|
|
320
387
|
}
|
|
321
388
|
else {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
389
|
+
const res = await client.post("/v1/skills/create", {
|
|
390
|
+
name, description, content,
|
|
391
|
+
...(allowedTools?.length ? { allowedTools } : {}),
|
|
392
|
+
});
|
|
393
|
+
if (res.status >= 200 && res.status < 300) {
|
|
394
|
+
result.created++;
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
const msg = res.data?.error ?? `HTTP ${res.status}`;
|
|
398
|
+
result.errors.push(`skill "${name}": ${friendlyError(msg)}`);
|
|
399
|
+
result.failed++;
|
|
400
|
+
}
|
|
325
401
|
}
|
|
326
402
|
}
|
|
327
403
|
return result;
|
|
@@ -513,9 +589,7 @@ export function registerDeployCommand(program) {
|
|
|
513
589
|
.option("--include-sessions", "Also deploy chat sessions")
|
|
514
590
|
.option("--all", "Deploy everything (full local→cloud migration)")
|
|
515
591
|
.action(async (opts) => {
|
|
516
|
-
|
|
517
|
-
// so a fresh user typing `polpo deploy` first goes through browser auth
|
|
518
|
-
// instead of getting a "Not logged in" wall.
|
|
592
|
+
clack.intro(pc.bold("Polpo — Deploy"));
|
|
519
593
|
const creds = await requireAuth({
|
|
520
594
|
context: "Deploying requires an authenticated session.",
|
|
521
595
|
});
|
|
@@ -523,17 +597,14 @@ export function registerDeployCommand(program) {
|
|
|
523
597
|
const polpoConfig = loadJson(path.join(polpoDir, "polpo.json"));
|
|
524
598
|
const projectName = polpoConfig?.project ?? path.basename(path.resolve(opts.dir));
|
|
525
599
|
const force = opts.force || opts.yes || false;
|
|
526
|
-
|
|
600
|
+
const interactive = !force && isTTY();
|
|
527
601
|
const cpClient = createApiClient(creds);
|
|
528
|
-
|
|
602
|
+
const s = clack.spinner();
|
|
529
603
|
// ── Step 1: Resolve project ────────────────────────
|
|
530
604
|
let projectId = polpoConfig?.projectId;
|
|
531
605
|
let projectSlug = polpoConfig?.projectSlug;
|
|
532
606
|
if (!projectId) {
|
|
533
607
|
try {
|
|
534
|
-
// pickOrg handles the 0-org case inline (prompts to create one),
|
|
535
|
-
// so a fresh user running `polpo deploy` against an empty account
|
|
536
|
-
// gets a single graceful prompt instead of an exit.
|
|
537
608
|
const org = await pickOrg(cpClient);
|
|
538
609
|
const project = await resolveOrCreateProject({
|
|
539
610
|
client: cpClient,
|
|
@@ -544,16 +615,16 @@ export function registerDeployCommand(program) {
|
|
|
544
615
|
});
|
|
545
616
|
projectId = project.id;
|
|
546
617
|
projectSlug = project.slug;
|
|
547
|
-
|
|
618
|
+
clack.log.success(`Project: ${pc.bold(project.name)}`);
|
|
548
619
|
}
|
|
549
620
|
catch (err) {
|
|
550
621
|
const msg = err instanceof Error ? err.message : String(err);
|
|
551
|
-
|
|
622
|
+
clack.outro(pc.red(friendlyError(msg)));
|
|
552
623
|
process.exit(1);
|
|
553
624
|
}
|
|
554
625
|
}
|
|
555
626
|
if (!projectId) {
|
|
556
|
-
|
|
627
|
+
clack.outro(pc.red("No project resolved. Deploy from a directory with .polpo/polpo.json"));
|
|
557
628
|
process.exit(1);
|
|
558
629
|
}
|
|
559
630
|
// Backfill `projectSlug` for users with legacy polpo.json (id only).
|
|
@@ -610,28 +681,27 @@ export function registerDeployCommand(program) {
|
|
|
610
681
|
}
|
|
611
682
|
}
|
|
612
683
|
if (detected.length > 0) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
684
|
+
clack.log.info(`Detected LLM keys:\n` +
|
|
685
|
+
detected.map(({ envVar, value }) => ` ${pc.dim(envVar.padEnd(25))} ${pc.bold(value.slice(0, 8))}...${value.slice(-4)}`).join("\n"));
|
|
686
|
+
let pushKeys = force;
|
|
687
|
+
if (!pushKeys && interactive) {
|
|
688
|
+
const answer = await clack.confirm({
|
|
689
|
+
message: "Push LLM keys to cloud?",
|
|
690
|
+
initialValue: true,
|
|
691
|
+
});
|
|
692
|
+
pushKeys = !clack.isCancel(answer) && !!answer;
|
|
693
|
+
}
|
|
694
|
+
if (pushKeys) {
|
|
695
|
+
s.start("Pushing LLM keys...");
|
|
696
|
+
let n = 0;
|
|
697
|
+
for (const { provider, value } of detected) {
|
|
698
|
+
try {
|
|
699
|
+
await cpClient.post("/v1/byok", { provider, key: value });
|
|
700
|
+
n++;
|
|
628
701
|
}
|
|
629
|
-
|
|
630
|
-
console.log(` Pushed ${n} LLM key(s)\n`);
|
|
631
|
-
}
|
|
632
|
-
else {
|
|
633
|
-
console.log();
|
|
702
|
+
catch { }
|
|
634
703
|
}
|
|
704
|
+
s.stop(n > 0 ? `Pushed ${n} LLM key(s)` : "No keys pushed");
|
|
635
705
|
}
|
|
636
706
|
}
|
|
637
707
|
// ── Step 3: Scan & show resources ────────────────────
|
|
@@ -655,105 +725,155 @@ export function registerDeployCommand(program) {
|
|
|
655
725
|
fs.readdirSync(path.join(polpoDir, "sessions")).length > 0;
|
|
656
726
|
const includeTasks = opts.all || opts.includeTasks;
|
|
657
727
|
const includeSessions = opts.all || opts.includeSessions;
|
|
658
|
-
|
|
728
|
+
// Build resource summary lines
|
|
729
|
+
const resourceLines = [];
|
|
659
730
|
if (hasAgents) {
|
|
660
731
|
const agentsData = loadJson(path.join(polpoDir, "agents.json"));
|
|
661
732
|
if (Array.isArray(agentsData)) {
|
|
662
733
|
const names = agentsData.map((e) => (e.agent ?? e).name).filter(Boolean);
|
|
663
|
-
|
|
734
|
+
resourceLines.push(` ${pc.bold("Agents")} ${names.length} ${pc.dim(`(${names.join(", ")})`)}`);
|
|
664
735
|
}
|
|
665
736
|
}
|
|
666
737
|
if (hasTeams) {
|
|
667
738
|
const teamsData = loadJson(path.join(polpoDir, "teams.json"));
|
|
668
739
|
if (Array.isArray(teamsData)) {
|
|
669
|
-
|
|
740
|
+
resourceLines.push(` ${pc.bold("Teams")} ${teamsData.length} ${pc.dim(`(${teamsData.map((t) => t.name).join(", ")})`)}`);
|
|
670
741
|
}
|
|
671
742
|
}
|
|
672
743
|
if (hasMemory)
|
|
673
|
-
|
|
744
|
+
resourceLines.push(` ${pc.bold("Memory")} ${pc.dim("shared + agent")}`);
|
|
674
745
|
if (hasMissions) {
|
|
675
746
|
const n = fs.readdirSync(path.join(polpoDir, "missions")).filter(f => f.endsWith(".json")).length;
|
|
676
|
-
|
|
747
|
+
resourceLines.push(` ${pc.bold("Missions")} ${n}`);
|
|
677
748
|
}
|
|
678
749
|
if (hasPlaybooks)
|
|
679
|
-
|
|
750
|
+
resourceLines.push(` ${pc.bold("Playbooks")} yes`);
|
|
680
751
|
if (hasSkills) {
|
|
681
752
|
const n = fs.readdirSync(path.join(polpoDir, "skills")).filter((d) => fs.statSync(path.join(polpoDir, "skills", d)).isDirectory()).length;
|
|
682
|
-
|
|
753
|
+
resourceLines.push(` ${pc.bold("Skills")} ${n}`);
|
|
683
754
|
}
|
|
684
755
|
if (hasSchedules) {
|
|
685
756
|
const n = fs.readdirSync(path.join(polpoDir, "schedules")).filter(f => f.endsWith(".json")).length;
|
|
686
|
-
|
|
757
|
+
resourceLines.push(` ${pc.bold("Schedules")} ${n}`);
|
|
687
758
|
}
|
|
688
759
|
if (hasVault)
|
|
689
|
-
|
|
760
|
+
resourceLines.push(` ${pc.bold("Vault")} ${pc.dim("encrypted credentials")}`);
|
|
690
761
|
if (hasAvatars)
|
|
691
|
-
|
|
762
|
+
resourceLines.push(` ${pc.bold("Avatars")} yes`);
|
|
692
763
|
if (includeTasks && hasTasks)
|
|
693
|
-
|
|
764
|
+
resourceLines.push(` ${pc.bold("Tasks")} yes`);
|
|
694
765
|
if (includeSessions && hasSessions)
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
766
|
+
resourceLines.push(` ${pc.bold("Sessions")} yes`);
|
|
767
|
+
if (resourceLines.length === 0) {
|
|
768
|
+
clack.outro(pc.yellow("Nothing to deploy — .polpo/ has no resources."));
|
|
769
|
+
process.exit(0);
|
|
770
|
+
}
|
|
771
|
+
clack.log.info(`Resources to deploy:\n${resourceLines.join("\n")}`);
|
|
772
|
+
if (interactive) {
|
|
773
|
+
const ok = await clack.confirm({
|
|
774
|
+
message: "Deploy these resources to cloud?",
|
|
775
|
+
initialValue: true,
|
|
776
|
+
});
|
|
777
|
+
if (clack.isCancel(ok) || !ok) {
|
|
778
|
+
clack.outro(pc.dim("Deploy cancelled."));
|
|
701
779
|
process.exit(0);
|
|
702
780
|
}
|
|
703
|
-
console.log();
|
|
704
781
|
}
|
|
705
|
-
// ── Step 4: Deploy
|
|
706
|
-
console.log(" Deploying...");
|
|
782
|
+
// ── Step 4: Deploy each resource ────────────────────
|
|
707
783
|
const total = emptyResult();
|
|
784
|
+
const conflictOpts = { force, interactive };
|
|
708
785
|
if (hasTeams) {
|
|
709
|
-
|
|
786
|
+
s.start("Deploying teams...");
|
|
787
|
+
const r = await deployTeams(client, polpoDir, conflictOpts);
|
|
788
|
+
mergeResult(total, r);
|
|
789
|
+
s.stop(`Teams: ${r.created} created, ${r.updated} updated${r.skipped ? `, ${r.skipped} skipped` : ""}${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
710
790
|
}
|
|
711
791
|
if (hasAgents) {
|
|
712
|
-
|
|
792
|
+
s.start("Deploying agents...");
|
|
793
|
+
const r = await deployAgents(client, polpoDir, conflictOpts);
|
|
794
|
+
mergeResult(total, r);
|
|
795
|
+
s.stop(`Agents: ${r.created} created, ${r.updated} updated${r.skipped ? `, ${r.skipped} skipped` : ""}${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
713
796
|
}
|
|
714
797
|
if (hasMemory) {
|
|
715
|
-
|
|
798
|
+
s.start("Deploying memory...");
|
|
799
|
+
const r = await deployMemory(client, polpoDir, conflictOpts);
|
|
800
|
+
mergeResult(total, r);
|
|
801
|
+
s.stop(`Memory: ${r.updated} updated${r.skipped ? `, ${r.skipped} skipped` : ""}${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
716
802
|
}
|
|
717
803
|
if (hasMissions) {
|
|
718
|
-
|
|
804
|
+
s.start("Deploying missions...");
|
|
805
|
+
const r = await deployMissions(client, polpoDir);
|
|
806
|
+
mergeResult(total, r);
|
|
807
|
+
s.stop(`Missions: ${r.created} created${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
719
808
|
}
|
|
720
809
|
if (hasPlaybooks) {
|
|
721
|
-
|
|
810
|
+
s.start("Deploying playbooks...");
|
|
811
|
+
const r = await deployPlaybooks(client, polpoDir);
|
|
812
|
+
mergeResult(total, r);
|
|
813
|
+
s.stop(`Playbooks: ${r.created} created${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
722
814
|
}
|
|
723
815
|
if (hasSkills) {
|
|
724
|
-
|
|
816
|
+
s.start("Deploying skills...");
|
|
817
|
+
const r = await deploySkills(client, polpoDir, conflictOpts);
|
|
818
|
+
mergeResult(total, r);
|
|
819
|
+
s.stop(`Skills: ${r.created} created, ${r.updated} updated${r.skipped ? `, ${r.skipped} skipped` : ""}${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
725
820
|
}
|
|
726
821
|
if (hasSchedules) {
|
|
727
|
-
|
|
822
|
+
s.start("Deploying schedules...");
|
|
823
|
+
const r = await deploySchedules(client, polpoDir);
|
|
824
|
+
mergeResult(total, r);
|
|
825
|
+
s.stop(`Schedules: ${r.created} created${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
728
826
|
}
|
|
729
827
|
if (hasVault) {
|
|
730
|
-
|
|
828
|
+
s.start("Deploying vault...");
|
|
829
|
+
const r = await deployVault(client, polpoDir);
|
|
830
|
+
mergeResult(total, r);
|
|
831
|
+
s.stop(`Vault: ${r.created} created${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
731
832
|
}
|
|
732
833
|
if (hasAvatars) {
|
|
733
|
-
|
|
834
|
+
s.start("Deploying avatars...");
|
|
835
|
+
const r = await deployAvatars(client, polpoDir, creds.baseUrl, creds.apiKey);
|
|
836
|
+
mergeResult(total, r);
|
|
837
|
+
s.stop(`Avatars: ${r.created} uploaded${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
734
838
|
}
|
|
735
839
|
if (includeTasks && hasTasks) {
|
|
736
|
-
|
|
840
|
+
s.start("Deploying tasks...");
|
|
841
|
+
const r = await deployTasks(client, polpoDir);
|
|
842
|
+
mergeResult(total, r);
|
|
843
|
+
s.stop(`Tasks: ${r.created} created${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
737
844
|
}
|
|
738
845
|
if (includeSessions && hasSessions) {
|
|
739
|
-
|
|
846
|
+
s.start("Deploying sessions...");
|
|
847
|
+
const r = await deploySessions(client, polpoDir);
|
|
848
|
+
mergeResult(total, r);
|
|
849
|
+
s.stop(`Sessions: ${r.created} imported${r.failed ? `, ${r.failed} failed` : ""}`);
|
|
740
850
|
}
|
|
741
851
|
// ── Summary ────────────────────────
|
|
742
|
-
|
|
852
|
+
if (total.errors.length > 0) {
|
|
853
|
+
clack.log.warn(`Errors:\n` +
|
|
854
|
+
total.errors.map(e => ` ${pc.red("x")} ${e}`).join("\n"));
|
|
855
|
+
}
|
|
856
|
+
const summaryParts = [];
|
|
743
857
|
if (total.created > 0)
|
|
744
|
-
|
|
858
|
+
summaryParts.push(`${total.created} created`);
|
|
745
859
|
if (total.updated > 0)
|
|
746
|
-
|
|
860
|
+
summaryParts.push(`${total.updated} updated`);
|
|
747
861
|
if (total.skipped > 0)
|
|
748
|
-
|
|
862
|
+
summaryParts.push(`${total.skipped} skipped`);
|
|
749
863
|
if (total.failed > 0)
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
864
|
+
summaryParts.push(pc.red(`${total.failed} failed`));
|
|
865
|
+
const endpoint = projectSlug ? `https://${projectSlug}.polpo.cloud` : "";
|
|
866
|
+
const outroLines = [];
|
|
867
|
+
if (total.failed === 0) {
|
|
868
|
+
outroLines.push(pc.green(`✓ Deployed: ${summaryParts.join(", ")}`));
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
outroLines.push(pc.yellow(`Deployed with errors: ${summaryParts.join(", ")}`));
|
|
872
|
+
}
|
|
873
|
+
if (endpoint) {
|
|
874
|
+
outroLines.push(pc.dim(` Endpoint: ${pc.bold(endpoint)}`));
|
|
755
875
|
}
|
|
756
|
-
|
|
876
|
+
clack.outro(outroLines.join("\n"));
|
|
757
877
|
process.exit(total.failed > 0 ? 1 : 0);
|
|
758
878
|
});
|
|
759
879
|
}
|