@desplega.ai/agent-swarm 2.7.2 → 2.7.3
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 +6 -0
- package/dist/{acp-adapter-624yge7a.js → acp-adapter-pdzw76ja.js} +1 -1
- package/dist/{actions-rd9xn281.js → actions-a90cfpca.js} +21 -21
- package/dist/{amp-adapter-k27hc00p.js → amp-adapter-4vtywcjk.js} +2 -2
- package/dist/{app-sjm3xc7g.js → app-8k68gygk.js} +3 -3
- package/dist/{assistant-6jbzs8wa.js → assistant-ck01q2yg.js} +32 -21
- package/dist/{boot-reembed-e2348eqc.js → boot-reembed-33spavpd.js} +1 -1
- package/dist/{boot-reembed-w53pd5xt.js → boot-reembed-xzge83a2.js} +1 -1
- package/dist/{boot-scrub-sweep-cpy4yg97.js → boot-scrub-sweep-mxvb8dr3.js} +3 -3
- package/dist/{boot-seeds-f6j4t6k6.js → boot-seeds-rpm8bb5r.js} +4 -4
- package/dist/{claude-adapter-v3ac8esd.js → claude-adapter-q12bcfj9.js} +3 -3
- package/dist/{claude-managed-setup-yyrkqvtm.js → claude-managed-setup-11vb6q89.js} +1 -1
- package/dist/{claude-sdk-session-85j69v6d.js → claude-sdk-session-gkv1107j.js} +10325 -10243
- package/dist/{cli-rs59z1qx.js → cli-07pcqwaj.js} +1 -1
- package/dist/{cli-6vq3ymqm.js → cli-0tkm519n.js} +1 -1
- package/dist/{cli-s7cm1927.js → cli-0xzbk52h.js} +4 -4
- package/dist/{cli-g0zc1b40.js → cli-0zae6bs6.js} +2 -2
- package/dist/{cli-p8yrsmfn.js → cli-10kjx0dm.js} +1 -1
- package/dist/{cli-7xsq63pe.js → cli-157ykn37.js} +10 -9
- package/dist/{cli-vjcj9k2p.js → cli-2h0rfmqk.js} +1 -1
- package/dist/{cli-9wasax9f.js → cli-35nzvqaf.js} +1 -1
- package/dist/{cli-e1p7dqe1.js → cli-4aydmmdy.js} +24 -3
- package/dist/{cli-942pp3bc.js → cli-54ax65rn.js} +3 -3
- package/dist/{cli-10cp92df.js → cli-5ke7gxwr.js} +6 -6
- package/dist/{cli-femw4q81.js → cli-6151a7dr.js} +2 -2
- package/dist/{cli-3cywb44q.js → cli-77eq8txs.js} +85 -3
- package/dist/{cli-atpfk4yd.js → cli-7cwrwb5x.js} +5 -5
- package/dist/{cli-bfbpvccx.js → cli-8a1k6mat.js} +61 -49
- package/dist/{cli-c18941tx.js → cli-8ch3b2r7.js} +3 -3
- package/dist/{cli-rs2s1t05.js → cli-9fk56y8c.js} +4 -4
- package/dist/{cli-0wx63vvw.js → cli-9km88w11.js} +1 -1
- package/dist/{cli-qy0z3mfj.js → cli-9vttw9nz.js} +56 -46
- package/dist/{cli-mzgppb7q.js → cli-a0w50zw9.js} +4 -4
- package/dist/{cli-z8a4mfz1.js → cli-aqp59jmv.js} +4 -4
- package/dist/{cli-5d8wd50e.js → cli-bs0fwb23.js} +1 -1
- package/dist/{cli-fa4kxxx3.js → cli-ct5920jb.js} +2 -2
- package/dist/{cli-0awbtmv2.js → cli-dek8hqjc.js} +10 -10
- package/dist/{cli-qp6adcmw.js → cli-e24w2mvk.js} +1 -1
- package/dist/{cli-t3npd8hm.js → cli-ghfhn1qc.js} +1 -1
- package/dist/{cli-jmcf8cnz.js → cli-ja369zab.js} +1 -1
- package/dist/{cli-wtjg6je3.js → cli-jhh7tbx6.js} +1 -1
- package/dist/{cli-e19bgatr.js → cli-jmk31131.js} +2 -2
- package/dist/{cli-2cj8fqv1.js → cli-kjcdzea8.js} +1 -1
- package/dist/{cli-1xpr5992.js → cli-kjha1q9k.js} +3 -3
- package/dist/{cli-9jfty8px.js → cli-ktcdczth.js} +538 -30
- package/dist/{cli-exd52qn4.js → cli-mcmpbpcc.js} +2 -2
- package/dist/{cli-2m2p3snc.js → cli-mmf43vcp.js} +6 -6
- package/dist/{cli-b2rhbaty.js → cli-pnm99spe.js} +10 -10
- package/dist/{cli-9jn9cj0q.js → cli-v9jk2k63.js} +1 -1
- package/dist/{cli-mt7bxjfd.js → cli-vhgcwppn.js} +1 -1
- package/dist/{cli-qa4hgda3.js → cli-vvz64zc5.js} +14 -13
- package/dist/{cli-txr0z97c.js → cli-wmhajw68.js} +171 -32
- package/dist/{cli-hzrmv8pg.js → cli-x38rmhap.js} +3 -3
- package/dist/{cli-hq9hd4hp.js → cli-xz6qjdqj.js} +1 -1
- package/dist/{cli-g9ypd5en.js → cli-z52gq55b.js} +4 -0
- package/dist/{cli-2qbmjfta.js → cli-z6219sd3.js} +4 -4
- package/dist/cli.js +19 -19
- package/dist/{codex-adapter-zm1bkdde.js → codex-adapter-46kbvvkx.js} +1 -1
- package/dist/{codex-hook-tkag7fxq.js → codex-hook-tm9ecrd5.js} +1 -1
- package/dist/{codex-session-runner-8k02mqw2.js → codex-session-runner-qy93hdrv.js} +1 -1
- package/dist/{commands-nw1dn4av.js → commands-hwdhacfx.js} +2 -2
- package/dist/{controller-3kkkmps0.js → controller-hjza5p1f.js} +3 -3
- package/dist/{controllers-connect-n2c4167r.js → controllers-connect-7m25cay2.js} +20 -20
- package/dist/{cursor-adapter-h5028emw.js → cursor-adapter-1qh7ps8w.js} +1 -1
- package/dist/{db-76y2msnf.js → db-w23k9301.js} +2 -2
- package/dist/{db-8rk0p8j1.js → db-wm8by383.js} +3 -3
- package/dist/{dispatcher-xp4wsx30.js → dispatcher-en25ebgc.js} +18 -18
- package/dist/{dsh-adapter-vyrr31bn.js → dsh-adapter-zkme32vd.js} +1 -1
- package/dist/{e2b-eesfj689.js → e2b-9z9mvt2p.js} +1 -1
- package/dist/{e2b-03nha1wz.js → e2b-yhvn70es.js} +1 -1
- package/dist/{embedding-space-ejefyep1.js → embedding-space-rgf4aypt.js} +6 -6
- package/dist/{embeddings-w297xaw0.js → embeddings-z9cmxhx1.js} +1 -1
- package/dist/{external-channel-mf9vp1fe.js → external-channel-g6vv85zv.js} +2 -2
- package/dist/{grok-adapter-mpzyyprz.js → grok-adapter-f6n1tpq3.js} +2 -2
- package/dist/handlers-ss5948rq.js +42 -0
- package/dist/{heartbeat-qnaycjzs.js → heartbeat-5xcdzz45.js} +21 -21
- package/dist/{hook-h6sqfzxr.js → hook-6423rme3.js} +2 -2
- package/dist/{hook-4zqdgea4.js → hook-8dk1csdd.js} +4 -4
- package/dist/{http-65ts210h.js → http-6mfh2ww9.js} +255 -60
- package/dist/{index-sqe7f6mj.js → index-0js6bgff.js} +1 -1
- package/dist/{index-farm1zsn.js → index-0ng1dd1t.js} +10937 -9527
- package/dist/{index-k3ewf1jg.js → index-5eyn1wc5.js} +18 -18
- package/dist/{index-vp4wa3qn.js → index-71gnebsn.js} +18 -18
- package/dist/{index-hdfad00b.js → index-q5gzpjdd.js} +12 -10
- package/dist/index-z3b1vfjx.js +44 -0
- package/dist/{keepalive-52k6skha.js → keepalive-cymckeg6.js} +3 -3
- package/dist/{lead-y9rj0gr1.js → lead-baknj2dh.js} +29 -29
- package/dist/{maintenance-f86wqfq6.js → maintenance-dfrnsx3n.js} +1 -1
- package/dist/{oauth-refresh-sweep-vddv9ky7.js → oauth-refresh-sweep-5dztk8x6.js} +2 -2
- package/dist/{onboard-8z2qk6gq.js → onboard-h36vja6y.js} +11 -6
- package/dist/{otel-impl-epet9j18.js → otel-impl-jerq8vz8.js} +1 -1
- package/dist/{pricing-refresh-6wm42a51.js → pricing-refresh-t3nx5krn.js} +3 -3
- package/dist/{rbac-roles-2kfcs1sc.js → rbac-roles-dw1awawd.js} +2 -2
- package/dist/{rbac-roles-13a0vj6d.js → rbac-roles-km0fc3e8.js} +1 -1
- package/dist/{registry-ajhexe44.js → registry-hzfh268v.js} +1 -1
- package/dist/{render-v2-cy8zsfsf.js → render-v2-65x4ayj8.js} +3 -3
- package/dist/{resume-c783qe6b.js → resume-61znk3hj.js} +6 -6
- package/dist/{session-access-s4tv0az8.js → session-access-4yc9htrg.js} +4 -4
- package/dist/{session-source-backfill-qzapz0ne.js → session-source-backfill-kr5bdjtp.js} +2 -2
- package/dist/{setup-st7htx1k.js → setup-9h61yr2p.js} +1 -1
- package/dist/{work-objects-ftj4zw70.js → work-objects-x7rb53qn.js} +27 -22
- package/dist/{worker-859fk25k.js → worker-7f3khgn0.js} +29 -29
- package/openapi.json +134 -3
- package/package.json +5 -5
- package/src/be/db/agents.ts +16 -1
- package/src/be/db/runtime.ts +25 -15
- package/src/be/db/session-summaries.ts +120 -0
- package/src/be/db/tasks/read.ts +7 -4
- package/src/be/db/tasks/search-index.ts +159 -0
- package/src/be/db/tasks/write.ts +3 -0
- package/src/be/db-queries/slack-inbound.ts +77 -0
- package/src/be/db.ts +27 -8
- package/src/be/mcp-overflow-sweep.ts +39 -0
- package/src/be/migrations/253_session_summaries.sql +300 -0
- package/src/be/migrations/254_query_audit_indexes.sql +25 -0
- package/src/be/migrations/256_agent_slack_icon.sql +2 -0
- package/src/be/migrations/257_agent_tasks_fts.sql +55 -0
- package/src/comb/config.ts +6 -2
- package/src/comb/requester.ts +69 -0
- package/src/comb/review-batch.ts +24 -2
- package/src/commands/onboard/agent-fs-version.ts +1 -1
- package/src/commands/onboard/compose-generator.ts +7 -2
- package/src/commands/onboard/env-generator.ts +8 -3
- package/src/commands/runner.ts +41 -24
- package/src/fs/agent-fs-provider.ts +9 -0
- package/src/heartbeat/heartbeat.ts +13 -6
- package/src/http/agents.ts +10 -2
- package/src/http/index.ts +15 -0
- package/src/http/integrations.ts +20 -0
- package/src/http/list-total-cache.ts +62 -0
- package/src/http/sessions.ts +9 -2
- package/src/http/slack-inbound.ts +1 -0
- package/src/http/status.ts +4 -1
- package/src/http/tasks.ts +12 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/queue-stall-alarm.ts +4 -1
- package/src/slack/actions.ts +2 -2
- package/src/slack/app-icon.ts +50 -0
- package/src/slack/app.ts +13 -1
- package/src/slack/assistant.ts +22 -1
- package/src/slack/event-dedup.test.ts +11 -0
- package/src/slack/event-dedup.ts +8 -4
- package/src/slack/inbound-dispatch.ts +58 -0
- package/src/slack/render-v2.ts +7 -8
- package/src/slack/responses.ts +21 -8
- package/src/slack/socket-ack.ts +237 -0
- package/src/slack/thread-buffer.ts +3 -5
- package/src/slack/watcher.ts +2 -2
- package/src/slack/work-objects.ts +5 -1
- package/src/tests/agent-fs-release-notes.test.ts +40 -0
- package/src/tests/agent-slack-icon.test.ts +85 -0
- package/src/tests/approval-cancellation-migration.test.ts +2 -0
- package/src/tests/asset-key-migration.test.ts +26 -0
- package/src/tests/comb-default.test.ts +159 -0
- package/src/tests/comb-review-batch.test.ts +192 -4
- package/src/tests/comb-status.test.ts +65 -2
- package/src/tests/db-agents.test.ts +13 -0
- package/src/tests/db-tasks-read.test.ts +10 -15
- package/src/tests/deploy-one-box.test.ts +161 -0
- package/src/tests/deploy-stubs.ts +68 -0
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/e2e-codex-auth-chain.test.ts +18 -1
- package/src/tests/fixtures/deploy-stubs/bun +3 -0
- package/src/tests/fixtures/deploy-stubs/bunx +27 -0
- package/src/tests/fixtures/deploy-stubs/curl +26 -0
- package/src/tests/fixtures/deploy-stubs/docker +122 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +5 -5
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +4 -4
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +12 -12
- package/src/tests/fixtures/onboard/full.compose.yml.txt +12 -12
- package/src/tests/fixtures/onboard/research.compose.yml.txt +4 -4
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +2 -2
- package/src/tests/heartbeat-expiry.test.ts +151 -25
- package/src/tests/http-api-integration.test.ts +31 -0
- package/src/tests/human-free-reclassify.test.ts +6 -0
- package/src/tests/install-sh-bind.test.ts +242 -0
- package/src/tests/install-sh-tunnel.test.ts +89 -0
- package/src/tests/kv-storage.test.ts +28 -0
- package/src/tests/list-endpoint-slimming.test.ts +3 -0
- package/src/tests/list-total-cache.test.ts +63 -0
- package/src/tests/mcp-overflow-sweep.test.ts +84 -0
- package/src/tests/onboard-compose.test.ts +17 -3
- package/src/tests/onboard-env.test.ts +13 -0
- package/src/tests/onboarding.test.ts +1 -1
- package/src/tests/preload.ts +1 -0
- package/src/tests/query-audit-plans.test.ts +314 -0
- package/src/tests/script-connections.test.ts +6 -0
- package/src/tests/session-summaries.test.ts +384 -0
- package/src/tests/sessions-tree.test.ts +5 -1
- package/src/tests/slack-app-icon.test.ts +90 -0
- package/src/tests/slack-external-channel-egress.test.ts +1 -1
- package/src/tests/slack-inbound-receipts.test.ts +34 -0
- package/src/tests/slack-inline-output.test.ts +29 -2
- package/src/tests/slack-render-v2-query-plans.test.ts +3 -2
- package/src/tests/slack-render-v2.test.ts +35 -0
- package/src/tests/slack-socket-ack.test.ts +553 -0
- package/src/tests/task-lineage.test.ts +3 -0
- package/src/tests/task-search-filter.test.ts +4 -4
- package/src/tests/task-search-fts.test.ts +320 -0
- package/src/tests/update-profile-auth.test.ts +28 -0
- package/src/tools/get-swarm.ts +1 -0
- package/src/tools/get-tasks.ts +6 -1
- package/src/tools/join-swarm.ts +1 -0
- package/src/tools/my-agent-info.ts +1 -0
- package/src/tools/slack-post.ts +2 -2
- package/src/tools/slack-reply.ts +2 -2
- package/src/tools/slack-start-thread.ts +2 -2
- package/src/tools/slack-update.ts +1 -1
- package/src/tools/update-profile.ts +20 -4
- package/src/tools/utils.ts +3 -4
- package/src/types.ts +47 -0
- package/src/utils/constants.ts +127 -5
- package/templates/official/lead/HEARTBEAT.md +4 -2
- package/templates/official/lead/config.json +2 -2
- package/templates/skills/swarm-docs/content.md +2 -0
- package/dist/handlers-7x8sywgr.js +0 -42
- package/dist/index-mc30r1hs.js +0 -44
|
@@ -71,7 +71,9 @@ const cases: {
|
|
|
71
71
|
params: [],
|
|
72
72
|
ids: ["a"],
|
|
73
73
|
},
|
|
74
|
-
|
|
74
|
+
// Search terms bind in the agent_tasks_fts candidate lookup; the page and
|
|
75
|
+
// count read the few candidates by rowid, so they bind no search params.
|
|
76
|
+
{ name: "search", filters: { search: "b" }, params: [], ids: ["b"] },
|
|
75
77
|
{
|
|
76
78
|
name: "combined ordering",
|
|
77
79
|
filters: {
|
|
@@ -82,17 +84,7 @@ const cases: {
|
|
|
82
84
|
source: ["api"],
|
|
83
85
|
requestedByUserId: "requester",
|
|
84
86
|
},
|
|
85
|
-
params: [
|
|
86
|
-
"pending",
|
|
87
|
-
"completed",
|
|
88
|
-
"%needle%",
|
|
89
|
-
"%needle%",
|
|
90
|
-
"%needle%",
|
|
91
|
-
"feature",
|
|
92
|
-
'%"blue"%',
|
|
93
|
-
"api",
|
|
94
|
-
"requester",
|
|
95
|
-
],
|
|
87
|
+
params: ["pending", "completed", "feature", '%"blue"%', "api", "requester"],
|
|
96
88
|
ids: ["c", "b"],
|
|
97
89
|
},
|
|
98
90
|
{
|
|
@@ -124,8 +116,10 @@ for (const { name, filters, params, ids, total } of cases) {
|
|
|
124
116
|
const countSpy = spyOn(client, "get");
|
|
125
117
|
try {
|
|
126
118
|
expect((await db.getAllTasks(filters)).map((task) => task.id)).toEqual(ids);
|
|
127
|
-
|
|
128
|
-
const
|
|
119
|
+
// A search runs its candidate lookup first; pick the page query by shape.
|
|
120
|
+
const listCall = listSpy.mock.calls.find(([query]) => query.includes(" OFFSET "));
|
|
121
|
+
expect(listCall?.[1]).toEqual(params);
|
|
122
|
+
const sql = listCall?.[0] ?? "";
|
|
129
123
|
expect(sql).toContain(`LIMIT ${filters.limit ?? 25} OFFSET ${filters.offset ?? 0}`);
|
|
130
124
|
expect(sql).toContain(
|
|
131
125
|
filters.orderBy === "createdAt"
|
|
@@ -133,7 +127,8 @@ for (const { name, filters, params, ids, total } of cases) {
|
|
|
133
127
|
: "ORDER BY lastUpdatedAt DESC, priority DESC",
|
|
134
128
|
);
|
|
135
129
|
expect(await db.getTasksCount(filters)).toBe(total ?? ids.length);
|
|
136
|
-
|
|
130
|
+
const countCall = countSpy.mock.calls.find(([query]) => query.includes("COUNT"));
|
|
131
|
+
expect(countCall?.[1]).toEqual(params);
|
|
137
132
|
expect((await db.getAllTasks({ ...filters, limit: 100, offset: 0 })).length).toBe(
|
|
138
133
|
await db.getTasksCount(filters),
|
|
139
134
|
);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import { makeDeployStubs } from "./deploy-stubs";
|
|
6
|
+
import { CHILD_PROCESS_TEST_BUDGET_MS, runChild } from "./test-proc";
|
|
7
|
+
|
|
8
|
+
// deploy/one-box/bootstrap.sh runs for real here, under a scratch root
|
|
9
|
+
// (AGENT_SWARM_BOOTSTRAP_ROOT) with stand-ins for docker, curl, apt-get, ufw,
|
|
10
|
+
// systemctl and caddy. By default it runs the repo's install.sh, whose onboard
|
|
11
|
+
// step is a stand-in for the CLI release on npm before AGENT_SWARM_BIND_ADDRESS:
|
|
12
|
+
// port mappings without a host address.
|
|
13
|
+
|
|
14
|
+
const ROOT = resolve(import.meta.dir, "../..");
|
|
15
|
+
const BOOTSTRAP = resolve(ROOT, "deploy/one-box/bootstrap.sh");
|
|
16
|
+
const read = (path: string) => Bun.file(resolve(ROOT, path)).text();
|
|
17
|
+
const API_KEY = "one-box-test-key";
|
|
18
|
+
const HOST = "203-0-113-7.sslip.io";
|
|
19
|
+
|
|
20
|
+
let dir: string;
|
|
21
|
+
let root: string;
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
dir = await mkdtemp(join(tmpdir(), "deploy-one-box-"));
|
|
25
|
+
root = join(dir, "root");
|
|
26
|
+
for (const sub of ["etc/agent-swarm", "etc/caddy", "etc/profile.d", "var/log", "root"]) {
|
|
27
|
+
await mkdir(join(root, sub), { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
await rm(dir, { recursive: true, force: true });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
async function bootstrap(installUrl = `file://${ROOT}/install.sh`) {
|
|
36
|
+
await Bun.write(
|
|
37
|
+
join(root, "etc/agent-swarm/deploy.env"),
|
|
38
|
+
`SWARM_DOMAIN=\nAGENT_SWARM_INSTALL_URL=${installUrl}\n`,
|
|
39
|
+
);
|
|
40
|
+
const stubs = await makeDeployStubs(dir, {
|
|
41
|
+
AGENT_SWARM_BOOTSTRAP_ROOT: root,
|
|
42
|
+
FAKE_API_KEY: API_KEY,
|
|
43
|
+
});
|
|
44
|
+
const result = await runChild(["bash", BOOTSTRAP], { env: stubs.env });
|
|
45
|
+
const status = (await readFile(join(root, "var/lib/agent-swarm/deploy-status"), "utf8")).trim();
|
|
46
|
+
return { ...result, out: result.stdout + result.stderr, status, stubs };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe("one-box bootstrap.sh", () => {
|
|
50
|
+
test(
|
|
51
|
+
"keeps every published port on loopback from the first docker compose up, with a CLI that ignores --bind",
|
|
52
|
+
async () => {
|
|
53
|
+
const r = await bootstrap();
|
|
54
|
+
expect(r.out).toContain(`Agent Swarm is running at https://${HOST}`);
|
|
55
|
+
expect(r.exitCode).toBe(0);
|
|
56
|
+
expect(r.status).toBe("done");
|
|
57
|
+
|
|
58
|
+
// Compose puts the stack on its own network, which daemon.json's "ip" does
|
|
59
|
+
// not cover: the first `up`, inside onboard, must find its default set.
|
|
60
|
+
expect((await r.stubs.ups())[0]).toBe("agent-swarm_default 127.0.0.1");
|
|
61
|
+
const history = await r.stubs.history();
|
|
62
|
+
expect(history.length).toBeGreaterThan(0);
|
|
63
|
+
expect(history.filter((p) => !p.startsWith("127.0.0.1:"))).toEqual([]);
|
|
64
|
+
expect(await r.stubs.ports()).toEqual([
|
|
65
|
+
"127.0.0.1:3013->3013/tcp",
|
|
66
|
+
"127.0.0.1:3201->3000/tcp",
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
expect(await readFile(join(root, "etc/caddy/Caddyfile"), "utf8")).toContain(
|
|
70
|
+
`${HOST} {\n\treverse_proxy 127.0.0.1:3013\n}`,
|
|
71
|
+
);
|
|
72
|
+
// The /setup link holds the API key: it goes to a root-only file, never stdout.
|
|
73
|
+
const link = await readFile(join(root, "root/agent-swarm-setup-link.txt"), "utf8");
|
|
74
|
+
expect(link).toContain(`/setup?apiUrl=https%3A%2F%2F${HOST}&apiKey=${API_KEY}`);
|
|
75
|
+
expect(r.out).not.toContain(API_KEY);
|
|
76
|
+
},
|
|
77
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
test(
|
|
81
|
+
"stops the stack when install.sh fails after publishing a port on a public interface",
|
|
82
|
+
async () => {
|
|
83
|
+
// A stand-in install.sh: starts a stack bound to 0.0.0.0 explicitly, then fails.
|
|
84
|
+
const script = join(dir, "failing-install.sh");
|
|
85
|
+
await Bun.write(
|
|
86
|
+
script,
|
|
87
|
+
[
|
|
88
|
+
"#!/usr/bin/env bash",
|
|
89
|
+
"set -euo pipefail",
|
|
90
|
+
'dir=""',
|
|
91
|
+
'while [ $# -gt 0 ]; do case "$1" in --dir) dir="$2"; shift 2 ;; *) shift ;; esac; done',
|
|
92
|
+
'mkdir -p "$dir" && cd "$dir"',
|
|
93
|
+
`printf 'services:\\n swarm-api:\\n ports:\\n - "0.0.0.0:3013:3013"\\n' >docker-compose.yml`,
|
|
94
|
+
"touch .env",
|
|
95
|
+
"docker compose --env-file .env up -d",
|
|
96
|
+
"echo 'API did not become healthy' >&2",
|
|
97
|
+
"exit 1",
|
|
98
|
+
"",
|
|
99
|
+
].join("\n"),
|
|
100
|
+
);
|
|
101
|
+
const r = await bootstrap(`file://${script}`);
|
|
102
|
+
expect(r.exitCode).not.toBe(0);
|
|
103
|
+
expect(r.status).toBe("failed");
|
|
104
|
+
expect(await r.stubs.history()).toEqual(["0.0.0.0:3013->3013/tcp"]);
|
|
105
|
+
expect(await r.stubs.ports()).toEqual([]);
|
|
106
|
+
expect(await r.stubs.calls()).toContain("compose stop");
|
|
107
|
+
expect(r.out).toContain("stopped the stack because it published ports on a public interface");
|
|
108
|
+
},
|
|
109
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
test(
|
|
113
|
+
"refuses an existing daemon.json without the loopback default before starting anything",
|
|
114
|
+
async () => {
|
|
115
|
+
await mkdir(join(root, "etc/docker"), { recursive: true });
|
|
116
|
+
await Bun.write(join(root, "etc/docker/daemon.json"), '{ "log-driver": "json-file" }\n');
|
|
117
|
+
const r = await bootstrap();
|
|
118
|
+
expect(r.exitCode).not.toBe(0);
|
|
119
|
+
expect(r.status).toBe("failed");
|
|
120
|
+
expect(r.out).toContain('without "ip": "127.0.0.1"');
|
|
121
|
+
expect(await r.stubs.history()).toEqual([]);
|
|
122
|
+
},
|
|
123
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
interface CloudConfig {
|
|
128
|
+
write_files: { path: string; permissions?: string; content: string }[];
|
|
129
|
+
runcmd: string[][];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
describe("one-box deploy packaging", () => {
|
|
133
|
+
test("cloud-init.yaml embeds bootstrap.sh byte for byte and runs it", async () => {
|
|
134
|
+
const yaml = await read("deploy/cloud-init.yaml");
|
|
135
|
+
expect(yaml.startsWith("#cloud-config\n")).toBe(true);
|
|
136
|
+
const config = Bun.YAML.parse(yaml) as CloudConfig;
|
|
137
|
+
const script = config.write_files.find(
|
|
138
|
+
(f) => f.path === "/usr/local/sbin/agent-swarm-bootstrap",
|
|
139
|
+
);
|
|
140
|
+
expect(script?.permissions).toBe("0700");
|
|
141
|
+
// On a mismatch, copy deploy/one-box/bootstrap.sh into the write_files entry (6-space indent).
|
|
142
|
+
expect(script?.content).toBe(await read("deploy/one-box/bootstrap.sh"));
|
|
143
|
+
expect(config.runcmd).toEqual([["/usr/local/sbin/agent-swarm-bootstrap"]]);
|
|
144
|
+
|
|
145
|
+
const settings = config.write_files.find((f) => f.path === "/etc/agent-swarm/deploy.env");
|
|
146
|
+
expect(settings?.permissions).toBe("0600");
|
|
147
|
+
expect(settings?.content).toContain("\nSWARM_DOMAIN=\n");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Structural only: CI has no packer binary to run `packer validate`.
|
|
151
|
+
test("the DigitalOcean image runs the same bootstrap.sh on first boot", async () => {
|
|
152
|
+
const template = await read("deploy/digitalocean/agent-swarm.pkr.hcl");
|
|
153
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Packer HCL syntax
|
|
154
|
+
expect(template).toContain('source = "${path.root}/../one-box/bootstrap.sh"');
|
|
155
|
+
expect(template).toContain('destination = "/opt/agent-swarm/bootstrap.sh"');
|
|
156
|
+
const onboot = await read(
|
|
157
|
+
"deploy/digitalocean/files/var/lib/cloud/scripts/per-instance/001_onboot",
|
|
158
|
+
);
|
|
159
|
+
expect(onboot).toContain("\n/opt/agent-swarm/bootstrap.sh\n");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, symlink } from "node:fs/promises";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
// A PATH of stand-ins (fixtures/deploy-stubs) so install.sh and
|
|
5
|
+
// deploy/one-box/bootstrap.sh run for real without Docker, root or a network.
|
|
6
|
+
// The fake docker records what the stack publishes; see its header.
|
|
7
|
+
|
|
8
|
+
const STUBS = resolve(import.meta.dir, "fixtures/deploy-stubs");
|
|
9
|
+
const NO_OPS = ["apt-get", "caddy", "systemctl", "ufw"];
|
|
10
|
+
|
|
11
|
+
export interface DeployStubs {
|
|
12
|
+
/** Environment for the child: the stub PATH plus the fake docker's settings. */
|
|
13
|
+
env: Record<string, string>;
|
|
14
|
+
/** The fake docker's state directory. */
|
|
15
|
+
state: string;
|
|
16
|
+
/** Published ports of the running stack, in `docker ps` form. */
|
|
17
|
+
ports(): Promise<string[]>;
|
|
18
|
+
/** Every port the stack ever published. */
|
|
19
|
+
history(): Promise<string[]>;
|
|
20
|
+
/** Every docker invocation. */
|
|
21
|
+
calls(): Promise<string[]>;
|
|
22
|
+
/** One "<network> <default host address>" line per `docker compose up`. */
|
|
23
|
+
ups(): Promise<string[]>;
|
|
24
|
+
/** Create a project network as an earlier `up` would, with this default address ("" for none). */
|
|
25
|
+
network(name: string, hostBinding: string): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function lines(path: string): Promise<string[]> {
|
|
29
|
+
const text = await readFile(path, "utf8").catch(() => "");
|
|
30
|
+
return text.split("\n").filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function makeDeployStubs(
|
|
34
|
+
dir: string,
|
|
35
|
+
env: Record<string, string> = {},
|
|
36
|
+
): Promise<DeployStubs> {
|
|
37
|
+
const bin = join(dir, "bin");
|
|
38
|
+
const state = join(dir, "docker-state");
|
|
39
|
+
await mkdir(bin, { recursive: true });
|
|
40
|
+
await mkdir(state, { recursive: true });
|
|
41
|
+
for (const name of ["docker", "curl", "bun", "bunx"]) {
|
|
42
|
+
await symlink(join(STUBS, name), join(bin, name));
|
|
43
|
+
}
|
|
44
|
+
for (const name of NO_OPS) {
|
|
45
|
+
await Bun.write(join(bin, name), "#!/bin/sh\nexit 0\n");
|
|
46
|
+
await chmod(join(bin, name), 0o755);
|
|
47
|
+
}
|
|
48
|
+
// bootstrap.sh insists on root.
|
|
49
|
+
await Bun.write(join(bin, "id"), "#!/bin/sh\necho 0\n");
|
|
50
|
+
await chmod(join(bin, "id"), 0o755);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
env: {
|
|
54
|
+
PATH: `${bin}:/usr/bin:/bin`,
|
|
55
|
+
HOME: dir,
|
|
56
|
+
FAKE_DOCKER_STATE: state,
|
|
57
|
+
...env,
|
|
58
|
+
},
|
|
59
|
+
state,
|
|
60
|
+
ports: () => lines(join(state, "ports")),
|
|
61
|
+
history: () => lines(join(state, "history")),
|
|
62
|
+
calls: () => lines(join(state, "calls")),
|
|
63
|
+
ups: () => lines(join(state, "ups")),
|
|
64
|
+
network: async (name, hostBinding) => {
|
|
65
|
+
await Bun.write(join(state, "networks", name), `${hostBinding}\n`);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
|
|
|
78
78
|
expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
test("OpenAPI and generated pages keep all
|
|
81
|
+
test("OpenAPI and generated pages keep all 467 operations", async () => {
|
|
82
82
|
const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
|
|
83
83
|
const operationKeys = [];
|
|
84
84
|
const tags = new Set();
|
|
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
|
|
|
103
103
|
expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
|
|
104
104
|
|
|
105
105
|
const generatedKeys = await generatedOperationKeys();
|
|
106
|
-
expect(operationKeys).toHaveLength(
|
|
107
|
-
expect(generatedKeys).toHaveLength(
|
|
106
|
+
expect(operationKeys).toHaveLength(467);
|
|
107
|
+
expect(generatedKeys).toHaveLength(467);
|
|
108
108
|
expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -376,7 +376,7 @@ describe("dsh harness", () => {
|
|
|
376
376
|
await rename(join(config.cwd, "dsh"), join(config.cwd, "npx"));
|
|
377
377
|
config.env = { ...config.env, DSH_BINARY: "", PATH: config.cwd };
|
|
378
378
|
await expect(new DshAdapter().createSession(config)).rejects.toThrow(
|
|
379
|
-
"dsh CLI not found. Install @deepseek-ai/dsh@0.2.
|
|
379
|
+
"dsh CLI not found. Install @deepseek-ai/dsh@0.2.2-alpha.2",
|
|
380
380
|
);
|
|
381
381
|
expect(await Bun.file(join(config.cwd, "invocation.json")).exists()).toBe(false);
|
|
382
382
|
});
|
|
@@ -288,18 +288,35 @@ describe("nightly workflow codex chain", () => {
|
|
|
288
288
|
const run = byName(all, "Run harness leg");
|
|
289
289
|
const save = byName(all, "Save Codex auth chain");
|
|
290
290
|
const verify = byName(all, "Verify Codex auth chain was saved");
|
|
291
|
+
const check = byName(all, "Check for a Codex auth chain to save");
|
|
291
292
|
const mark = byName(all, "Fail the codex result when the chain was not saved");
|
|
292
293
|
const upload = byName(all, "Upload harness results");
|
|
293
294
|
expect(all.indexOf(mark)).toBeGreaterThan(all.indexOf(verify));
|
|
294
295
|
expect(all.indexOf(mark)).toBeLessThan(all.indexOf(upload));
|
|
295
296
|
expect(mark.if).toBe(
|
|
296
|
-
`always() && (steps.${save.id}.outcome == 'failure' || steps.${verify.id}.outcome == 'failure')`,
|
|
297
|
+
`always() && (steps.${check.id}.outcome == 'failure' || steps.${save.id}.outcome == 'failure' || steps.${verify.id}.outcome == 'failure')`,
|
|
297
298
|
);
|
|
298
299
|
const resultsPath = run.run?.match(/--json (\S+)/)?.[1];
|
|
299
300
|
expect(resultsPath).toBeDefined();
|
|
300
301
|
expect(mark.run).toBe(`bun scripts/e2e/codex-chain-lost.ts ${resultsPath}`);
|
|
301
302
|
});
|
|
302
303
|
|
|
304
|
+
// The worker image runs steps as root and the harness writes the chain mode 600. hashFiles()
|
|
305
|
+
// runs on the host as the runner user, cannot read that file, and fails the whole condition.
|
|
306
|
+
test("the save is gated by an in-container file check, never hashFiles()", async () => {
|
|
307
|
+
const all = await steps();
|
|
308
|
+
const check = byName(all, "Check for a Codex auth chain to save");
|
|
309
|
+
const save = byName(all, "Save Codex auth chain");
|
|
310
|
+
expect(all.indexOf(check)).toBeLessThan(all.indexOf(save));
|
|
311
|
+
expect(check.if).toBe(
|
|
312
|
+
"always() && matrix.provider == 'codex' && github.ref == 'refs/heads/main'",
|
|
313
|
+
);
|
|
314
|
+
expect(check.run).toContain(`[ -f ${save.with?.path} ]`);
|
|
315
|
+
expect(check.run).toContain(`echo "present=true" >> "$GITHUB_OUTPUT"`);
|
|
316
|
+
expect(save.if).toBe(`always() && steps.${check.id}.outputs.present == 'true'`);
|
|
317
|
+
for (const step of all) expect(step.if ?? "").not.toContain("hashFiles(");
|
|
318
|
+
});
|
|
319
|
+
|
|
303
320
|
test("the codex leg and its secret stay on main", async () => {
|
|
304
321
|
const all = await steps();
|
|
305
322
|
const run = byName(all, "Run harness leg");
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fake `bunx --package @desplega.ai/agent-swarm agent-swarm onboard` for the
|
|
3
|
+
# deploy tests. Like the CLI release on npm before AGENT_SWARM_BIND_ADDRESS
|
|
4
|
+
# (2.7.1), it writes port mappings without a host address and starts the stack.
|
|
5
|
+
# After the stack is up, FAKE_ONBOARD_FAIL=1 makes it fail and
|
|
6
|
+
# FAKE_ONBOARD_FAIL=term sends SIGTERM to the installer, like a Ctrl-C.
|
|
7
|
+
# FAKE_ONBOARD_API_PORT replaces the API's "3013:3013" mapping.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
cat >docker-compose.yml <<YAML
|
|
10
|
+
services:
|
|
11
|
+
swarm-api:
|
|
12
|
+
image: ghcr.io/desplega-ai/agent-swarm:latest
|
|
13
|
+
ports:
|
|
14
|
+
- "${FAKE_ONBOARD_API_PORT:-3013:3013}"
|
|
15
|
+
volumes:
|
|
16
|
+
- swarm_data:/app/data
|
|
17
|
+
coder:
|
|
18
|
+
image: ghcr.io/desplega-ai/agent-swarm-worker:latest
|
|
19
|
+
ports:
|
|
20
|
+
- "3201:3000"
|
|
21
|
+
YAML
|
|
22
|
+
printf 'API_KEY=%s\nMCP_BASE_URL=http://localhost:3013\n' "${FAKE_API_KEY:-test-api-key}" >.env
|
|
23
|
+
docker compose --env-file .env up -d
|
|
24
|
+
case "${FAKE_ONBOARD_FAIL:-}" in
|
|
25
|
+
1) echo "onboard: health check failed" >&2; exit 1 ;;
|
|
26
|
+
term) kill -TERM "$PPID" ;;
|
|
27
|
+
esac
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fake curl for the deploy tests: the public IP lookup, file:// downloads and
|
|
3
|
+
# health checks. Anything else fails, so a test never reaches the network.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
out="" url=""
|
|
6
|
+
while [ $# -gt 0 ]; do
|
|
7
|
+
case "$1" in
|
|
8
|
+
-o | --output) out="$2"; shift 2 ;;
|
|
9
|
+
-m | --max-time) shift 2 ;;
|
|
10
|
+
-*) shift ;;
|
|
11
|
+
*) url="$1"; shift ;;
|
|
12
|
+
esac
|
|
13
|
+
done
|
|
14
|
+
case "$url" in
|
|
15
|
+
https://checkip.amazonaws.com* | https://api.ipify.org* | https://ipv4.icanhazip.com*)
|
|
16
|
+
echo "${FAKE_PUBLIC_IP:-203.0.113.7}"
|
|
17
|
+
;;
|
|
18
|
+
file://*)
|
|
19
|
+
if [ -n "$out" ] && [ "$out" != /dev/null ]; then cp "${url#file://}" "$out"; else cat "${url#file://}"; fi
|
|
20
|
+
;;
|
|
21
|
+
*/health) exit "${FAKE_HEALTH_EXIT:-0}" ;;
|
|
22
|
+
*)
|
|
23
|
+
echo "fake curl: no network in tests ($url)" >&2
|
|
24
|
+
exit 22
|
|
25
|
+
;;
|
|
26
|
+
esac
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Fake docker for the deploy tests (install.sh, deploy/one-box/bootstrap.sh).
|
|
3
|
+
# It reads port mappings from ./docker-compose.yml and the override file the way
|
|
4
|
+
# Compose and dockerd do, and keeps its state in $FAKE_DOCKER_STATE:
|
|
5
|
+
# calls every invocation
|
|
6
|
+
# ports published ports of the running stack, in `docker ps` form
|
|
7
|
+
# history every port ever published, so a test can see a short-lived exposure
|
|
8
|
+
# ups one line per `compose up`: the project network and its default address
|
|
9
|
+
# networks/ one file per network, holding its host_binding_ipv4 option
|
|
10
|
+
# Like dockerd, a port without a host address goes to its network's
|
|
11
|
+
# com.docker.network.bridge.host_binding_ipv4, else every interface. daemon.json
|
|
12
|
+
# "ip" covers only the default bridge, never a Compose project network. Like
|
|
13
|
+
# Compose before 2.31 (and any network without a config-hash label), an
|
|
14
|
+
# existing network is reused with its old options.
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
state="${FAKE_DOCKER_STATE:?FAKE_DOCKER_STATE is not set}"
|
|
17
|
+
printf '%s\n' "$*" >>"$state/calls"
|
|
18
|
+
mkdir -p "$state/networks"
|
|
19
|
+
touch "$state/ports" "$state/history" "$state/ups"
|
|
20
|
+
|
|
21
|
+
# AGENT_SWARM_BIND_ADDRESS as Compose sees it: the shell wins over .env.
|
|
22
|
+
bind_address() {
|
|
23
|
+
if [ -n "${AGENT_SWARM_BIND_ADDRESS+x}" ]; then
|
|
24
|
+
printf '%s' "$AGENT_SWARM_BIND_ADDRESS"
|
|
25
|
+
elif [ -f .env ]; then
|
|
26
|
+
grep -E '^AGENT_SWARM_BIND_ADDRESS=' .env | tail -n 1 | cut -d= -f2- || true
|
|
27
|
+
fi
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# docker-compose.yml plus the first override file Compose finds (compose-go
|
|
31
|
+
# loads only one).
|
|
32
|
+
compose_files() {
|
|
33
|
+
local f
|
|
34
|
+
echo docker-compose.yml
|
|
35
|
+
for f in compose.override.yml compose.override.yaml docker-compose.override.yml docker-compose.override.yaml; do
|
|
36
|
+
if [ -f "$f" ]; then
|
|
37
|
+
echo "$f"
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
project_network() {
|
|
44
|
+
local p
|
|
45
|
+
p="$(printf '%s' "${COMPOSE_PROJECT_NAME:-$(basename "$PWD")}" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9_-')"
|
|
46
|
+
printf '%s_default' "${p#"${p%%[a-z0-9]*}"}"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
compose_up() {
|
|
50
|
+
# shellcheck disable=SC2016 # the literal prefix the onboard generator writes
|
|
51
|
+
local prefix='${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}'
|
|
52
|
+
local addr net dip entry a b c f files=()
|
|
53
|
+
addr="$(bind_address)"
|
|
54
|
+
while IFS= read -r f; do files+=("$f"); done < <(compose_files)
|
|
55
|
+
net="$(project_network)"
|
|
56
|
+
if [ ! -f "$state/networks/$net" ]; then
|
|
57
|
+
sed -nE 's/^ *com\.docker\.network\.bridge\.host_binding_ipv4: *"?([^"]*)"? *$/\1/p' "${files[@]}" |
|
|
58
|
+
tail -n 1 >"$state/networks/$net"
|
|
59
|
+
fi
|
|
60
|
+
dip="$(cat "$state/networks/$net")"
|
|
61
|
+
dip="${dip:-0.0.0.0}"
|
|
62
|
+
printf '%s %s\n' "$net" "$dip" >>"$state/ups"
|
|
63
|
+
awk '/^ *ports: *$/ { p = 1; next }
|
|
64
|
+
p && /^ *- / { e = $0; sub(/^ *- */, "", e); gsub(/"/, "", e); print e; next }
|
|
65
|
+
{ p = 0 }' "${files[@]}" |
|
|
66
|
+
while IFS= read -r entry; do
|
|
67
|
+
if [ "${entry#"$prefix"}" != "$entry" ]; then
|
|
68
|
+
entry="${entry#"$prefix"}"
|
|
69
|
+
[ -z "$addr" ] || entry="${addr}:${entry}"
|
|
70
|
+
fi
|
|
71
|
+
IFS=: read -r a b c <<<"$entry"
|
|
72
|
+
if [ -n "$c" ]; then
|
|
73
|
+
printf '%s:%s->%s/tcp\n' "$a" "$b" "$c"
|
|
74
|
+
else
|
|
75
|
+
printf '%s:%s->%s/tcp\n' "$dip" "$a" "$b"
|
|
76
|
+
fi
|
|
77
|
+
done >"$state/ports"
|
|
78
|
+
cat "$state/ports" >>"$state/history"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if [ "${1:-}" = compose ]; then
|
|
82
|
+
shift
|
|
83
|
+
verb=""
|
|
84
|
+
while [ $# -gt 0 ]; do
|
|
85
|
+
case "$1" in
|
|
86
|
+
--env-file | -f | -p) shift 2 ;;
|
|
87
|
+
-*) shift ;;
|
|
88
|
+
*) verb="$1"; break ;;
|
|
89
|
+
esac
|
|
90
|
+
done
|
|
91
|
+
case "$verb" in
|
|
92
|
+
version) echo "Docker Compose version v2.30.0" ;;
|
|
93
|
+
up) compose_up ;;
|
|
94
|
+
ps) cat "$state/ports" ;;
|
|
95
|
+
stop) : >"$state/ports" ;;
|
|
96
|
+
down) : >"$state/ports"; rm -f "$state/networks/$(project_network)" ;;
|
|
97
|
+
*) ;;
|
|
98
|
+
esac
|
|
99
|
+
exit 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
case "${1:-}" in
|
|
103
|
+
ps) cat "$state/ports" ;;
|
|
104
|
+
network)
|
|
105
|
+
# docker network inspect [--format <template>] <name>
|
|
106
|
+
[ "${2:-}" = inspect ] || exit 0
|
|
107
|
+
format="" name=""
|
|
108
|
+
shift 2
|
|
109
|
+
while [ $# -gt 0 ]; do
|
|
110
|
+
case "$1" in
|
|
111
|
+
--format | -f) format="$2"; shift 2 ;;
|
|
112
|
+
*) name="$1"; shift ;;
|
|
113
|
+
esac
|
|
114
|
+
done
|
|
115
|
+
if [ ! -f "$state/networks/$name" ]; then
|
|
116
|
+
echo "Error response from daemon: network $name not found" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
if [ -n "$format" ]; then cat "$state/networks/$name"; else echo "[{\"Name\": \"$name\"}]"; fi
|
|
120
|
+
;;
|
|
121
|
+
*) ;;
|
|
122
|
+
esac
|
|
@@ -32,7 +32,7 @@ services:
|
|
|
32
32
|
- APP_URL=https://app.agent-swarm.dev
|
|
33
33
|
|
|
34
34
|
ports:
|
|
35
|
-
- "3013:3013"
|
|
35
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3013:3013"
|
|
36
36
|
|
|
37
37
|
volumes:
|
|
38
38
|
- swarm_data:/app/data
|
|
@@ -73,7 +73,7 @@ services:
|
|
|
73
73
|
- MAX_CONCURRENT_TASKS=2
|
|
74
74
|
|
|
75
75
|
ports:
|
|
76
|
-
- "3201:3000"
|
|
76
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
|
|
77
77
|
|
|
78
78
|
volumes:
|
|
79
79
|
- swarm_logs:/logs
|
|
@@ -109,7 +109,7 @@ services:
|
|
|
109
109
|
- MAX_CONCURRENT_TASKS=1
|
|
110
110
|
|
|
111
111
|
ports:
|
|
112
|
-
- "3202:3000"
|
|
112
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3202:3000"
|
|
113
113
|
|
|
114
114
|
volumes:
|
|
115
115
|
- swarm_logs:/logs
|
|
@@ -145,7 +145,7 @@ services:
|
|
|
145
145
|
- MAX_CONCURRENT_TASKS=1
|
|
146
146
|
|
|
147
147
|
ports:
|
|
148
|
-
- "3203:3000"
|
|
148
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3203:3000"
|
|
149
149
|
|
|
150
150
|
volumes:
|
|
151
151
|
- swarm_logs:/logs
|
|
@@ -181,7 +181,7 @@ services:
|
|
|
181
181
|
- MAX_CONCURRENT_TASKS=1
|
|
182
182
|
|
|
183
183
|
ports:
|
|
184
|
-
- "3204:3000"
|
|
184
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3204:3000"
|
|
185
185
|
|
|
186
186
|
volumes:
|
|
187
187
|
- swarm_logs:/logs
|
|
@@ -32,7 +32,7 @@ services:
|
|
|
32
32
|
- APP_URL=https://app.agent-swarm.dev
|
|
33
33
|
|
|
34
34
|
ports:
|
|
35
|
-
- "3013:3013"
|
|
35
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3013:3013"
|
|
36
36
|
|
|
37
37
|
volumes:
|
|
38
38
|
- swarm_data:/app/data
|
|
@@ -73,7 +73,7 @@ services:
|
|
|
73
73
|
- MAX_CONCURRENT_TASKS=2
|
|
74
74
|
|
|
75
75
|
ports:
|
|
76
|
-
- "3201:3000"
|
|
76
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3201:3000"
|
|
77
77
|
|
|
78
78
|
volumes:
|
|
79
79
|
- swarm_logs:/logs
|
|
@@ -109,7 +109,7 @@ services:
|
|
|
109
109
|
- MAX_CONCURRENT_TASKS=1
|
|
110
110
|
|
|
111
111
|
ports:
|
|
112
|
-
- "3202:3000"
|
|
112
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3202:3000"
|
|
113
113
|
|
|
114
114
|
volumes:
|
|
115
115
|
- swarm_logs:/logs
|
|
@@ -145,7 +145,7 @@ services:
|
|
|
145
145
|
- MAX_CONCURRENT_TASKS=1
|
|
146
146
|
|
|
147
147
|
ports:
|
|
148
|
-
- "3203:3000"
|
|
148
|
+
- "${AGENT_SWARM_BIND_ADDRESS:+${AGENT_SWARM_BIND_ADDRESS:-}:}3203:3000"
|
|
149
149
|
|
|
150
150
|
volumes:
|
|
151
151
|
- swarm_logs:/logs
|