Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion .github/scripts/docker-profile.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import assert from 'node:assert/strict'
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { imagePackages, linkPackages, prepareProfile } from '../../docker/profile.mjs'
import { imagePackages, linkPackages, missingDependencies, prepareProfile } from '../../docker/profile.mjs'

const root = new URL('../../', import.meta.url)
const read = (path) => readFileSync(new URL(path, root), 'utf8')
Expand All @@ -27,6 +27,30 @@ test('imagePackages lists the unpacked packages by name', () => {
} finally { rmSync(dir, { recursive: true, force: true }) }
})

test('missingDependencies names each declared runtime dependency a package does not carry', () => {
const dir = packagesDir()
try {
const core = join(dir, 'ds-bot-0.2.1')
writeFileSync(join(core, 'package.json'), JSON.stringify({ name: 'ds-bot', dependencies: { '@larksuiteoapi/node-sdk': '1.0.0', ws: '8.0.0' }, peerDependencies: { '@deepseek-ai/dsh-llm': '*' } }))
assert.deepEqual(missingDependencies(imagePackages(dir)), ['ds-bot -> @larksuiteoapi/node-sdk', 'ds-bot -> ws'], 'a linked package brings none of its dependencies')
for (const dep of ['@larksuiteoapi/node-sdk', 'ws']) {
mkdirSync(join(core, 'node_modules', dep), { recursive: true })
writeFileSync(join(core, 'node_modules', dep, 'package.json'), '{}')
}
assert.deepEqual(missingDependencies(imagePackages(dir)), [], 'peers are not checked: the DSH profile provides them')
} finally { rmSync(dir, { recursive: true, force: true }) }
})

test('the image installs ds-bot runtime dependencies from the lockfile and checks them', () => {
// Without this the Feishu long connection fails in the container: the SDK import is lazy, so
// the image starts healthy and only the connection reports an error.
const dockerfile = read('Dockerfile')
assert.match(dockerfile, /cd core \\\n && npm ci --omit=dev --ignore-scripts/, 'the pinned runtime dependencies, without build tools or install scripts')
assert.match(dockerfile, /mv node_modules "\$\(echo \/out\/ds-bot-\*\)\/node_modules"/, 'next to the linked package, where its imports resolve')
assert.match(dockerfile, /missingDependencies\(imagePackages\('\/out'\)\)/, 'the build stops on a missing dependency')
assert.match(read('core/.npmrc'), /^legacy-peer-deps=true$/m, 'the @deepseek-ai peers stay out of the image package')
})

test('a new profile gets the web bundles plus every image package, linked', () => {
const next = linkPackages(undefined, [{ name: 'ds-bot', dir: '/opt/p/core' }, { name: '@flybot/tree', dir: '/opt/p/tree' }])
assert.deepEqual(next.dependencies, { 'ds-bot': 'link:/opt/p/core', '@flybot/tree': 'link:/opt/p/tree' })
Expand Down
3 changes: 3 additions & 0 deletions .github/scripts/openapi.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ test('FBA safe/watch contracts preserve native asymmetries and human/host-only b
assert.deepEqual(op('project-role').humanRoles, ['owner'])
for (const endpoint of ['personal-agent-token-create', 'personal-agent-delegation-set', 'project-close-prepare', 'project-close-confirm', 'project-terminal-open', 'project-agent-history']) assert.equal(op(endpoint).personalAgent.watch, false, endpoint)
assert.deepEqual(op('personal-agent-propose-task').security, [{ personalAgentToken: [] }])
assert.equal(op('feishu').personalAgent.safe, false)
assert.equal(op('feishu').personalAgent.watch, false)
assert.deepEqual(op('feishu').security, [{ memberSession: [] }])
for (const name of ['claim', 'handoff.offer', 'handoff.accept', 'complete', 'arbitrate', 'split']) assert.throws(() => op('project-development', 'command', name), /Unspecified/)
assert.equal(spec.components.schemas.PersonalAgentTokenCreateResult.properties.token.readOnly, true)
assert.equal(spec.components.schemas.PersonalAgentTokenCreateResult.properties.token.writeOnly, undefined)
Expand Down
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ RUN node .github/scripts/check-pack.mjs /pkgs \
&& for tgz in /pkgs/*.tgz; do \
dir="/out/$(basename "$tgz" .tgz)"; mkdir -p "$dir" && tar -xzf "$tgz" -C "$dir" --strip-components=1; \
done
# The profile links these packages, and a `link:` installs none of a linked package's own
# dependencies. So ds-bot's runtime ones (the Feishu long-connection SDK, the Claude and Codex
# executor SDKs) go into its node_modules here, at the versions core/package-lock.json pins;
# core/.npmrc keeps the @deepseek-ai peers out, DSH provides those. The build stops if any
# image package still lacks a declared dependency.
RUN cd core \
&& npm ci --omit=dev --ignore-scripts --no-audit --no-fund \
&& mv node_modules "$(echo /out/ds-bot-*)/node_modules" \
&& node --input-type=module -e "import { imagePackages, missingDependencies } from '/src/docker/profile.mjs'; const missing = missingDependencies(imagePackages('/out')); if (missing.length) { console.error('runtime dependencies missing from the image: ' + missing.join(', ')); process.exit(1) }"

# Stage 2: the runtime.
FROM ${NODE_IMAGE}
Expand Down
1 change: 1 addition & 0 deletions changelog.d/docker-local-api-trust.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Require an explicit trusted browser address for the local Docker override so signing in through a published port also permits authenticated API and WebSocket requests, while other hosts and cross-origin requests remain blocked.
1 change: 1 addition & 0 deletions changelog.d/docker-runtime-deps.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Docker image now carries the FlyBot core's runtime dependencies. The profile links the packages, and a link installs none of their own dependencies, so a container started healthy but could not open the Feishu long connection (and could not load the Claude or Codex executor SDKs). The build now fails if a declared dependency is missing, and a Feishu connection that fails because the SDK is absent says so in the diagnostics instead of pointing at the network and credentials.
1 change: 1 addition & 0 deletions changelog.d/feishu-migration.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add guided Feishu application setup with long-connection and HTTP reception, persistent threaded Bot routing, rich messages and attachments, linked card submissions, delivery reactions, and application-scoped calendar, document, table, meeting and task tools with native write approvals. Retain custom bot webhooks and add a local Docker port-publishing override. Use one Connect entry with compact permission choices, optional JSON preview and responsive setup navigation.
Loading
Loading