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
18 changes: 10 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ jobs:
name: TypeScript
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.9"

- name: Cache Bun dependencies
uses: actions/cache@v4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
Expand All @@ -39,14 +41,14 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: actions/setup-python@v5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"

- name: Install linter
run: pip install ruff
run: pip install ruff==0.9.10

- name: Lint
run: ruff check src
Expand All @@ -59,9 +61,9 @@ jobs:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: actions/setup-python@v5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: "pip"
Expand All @@ -36,10 +36,10 @@ jobs:
run: mkdocs build -f docs/mkdocs.yml -d site

- name: Setup Pages
uses: actions/configure-pages@v5
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
with:
path: docs/site

Expand All @@ -52,4 +52,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
9 changes: 5 additions & 4 deletions .github/workflows/publish-engine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ jobs:
permissions:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: actions/setup-python@v5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: packages/altimate-engine/pyproject.toml

- name: Install build tools
run: pip install build
run: pip install build==1.2.2

- name: Build package
run: python -m build
working-directory: packages/altimate-engine

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
with:
packages-dir: packages/altimate-engine/dist/
skip-existing: true
42 changes: 23 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ concurrency:
group: release
cancel-in-progress: false

permissions:
contents: write
id-token: write

env:
GH_REPO: AltimateAI/altimate-code

jobs:
build:
name: Build (${{ matrix.os }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
fail-fast: false
matrix:
os: [linux, darwin, win32]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.9"

- name: Cache Bun dependencies
uses: actions/cache@v4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
Expand All @@ -50,7 +50,7 @@ jobs:
MODELS_DEV_API_JSON: test/tool/fixtures/models-api.json

- name: Upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: dist-${{ matrix.os }}
path: packages/altimate-code/dist/
Expand All @@ -59,13 +59,17 @@ jobs:
name: Publish to npm
needs: build
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: oven-sh/setup-bun@v2
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.9"

- name: Cache Bun dependencies
uses: actions/cache@v4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.bun/install/cache
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
Expand All @@ -76,7 +80,7 @@ jobs:
run: bun install

- name: Download all build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: dist-*
path: packages/altimate-code/dist/
Expand Down Expand Up @@ -124,23 +128,23 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- uses: actions/setup-python@v5
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
cache: 'pip'
cache-dependency-path: packages/altimate-engine/pyproject.toml

- name: Install build tools
run: pip install build
run: pip install build==1.2.2

- name: Build package
run: python -m build
working-directory: packages/altimate-engine

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
with:
packages-dir: packages/altimate-engine/dist/
skip-existing: true
Expand All @@ -152,7 +156,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

Expand Down Expand Up @@ -206,14 +210,14 @@ jobs:
CURRENT_TAG: ${{ github.ref_name }}

- name: Download all build artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
pattern: dist-*
path: packages/altimate-code/dist/
merge-multiple: true

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
with:
body_path: notes.md
draft: false
Expand Down
18 changes: 16 additions & 2 deletions packages/altimate-code/src/cli/cmd/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,23 @@ export const AuthLoginCommand = cmd({
prompts.intro("Add credential")
if (args.url) {
const wellknown = await fetch(`${args.url}/.well-known/altimate-code`).then((x) => x.json() as any)
prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``)
const raw = wellknown?.auth?.command
if (!Array.isArray(raw) || !raw.every((c: unknown) => typeof c === 'string')) {
prompts.log.warn('Invalid auth command from server')
prompts.outro('Done')
return
}
const cmd = raw as string[]
const confirm = await prompts.confirm({
message: `The server requests to run: ${cmd.join(" ")}. Allow?`,
})
if (prompts.isCancel(confirm) || !confirm) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The as string[] cast trusts the server response shape. Consider validating that wellknown.auth.command is actually an array of strings before using it — a malicious server could return unexpected types:

const raw = wellknown?.auth?.command
if (!Array.isArray(raw) || !raw.every((c: unknown) => typeof c === 'string')) {
  prompts.log.warn('Invalid auth command from server')
  prompts.outro('Done')
  return
}
const cmd = raw as string[]

The confirmation prompt is a great addition though — this was a real security gap.

prompts.log.warn("Aborted.")
prompts.outro("Done")
return
}
const proc = Bun.spawn({
cmd: wellknown.auth.command,
cmd,
stdout: "pipe",
})
const exit = await proc.exited
Expand Down
15 changes: 9 additions & 6 deletions packages/altimate-code/src/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,10 @@ export namespace MCP {
return
}

const result = await create(key, mcp).catch(() => undefined)
const result = await create(key, mcp).catch((e) => {
log.warn("failed to initialize MCP server", { key, error: e instanceof Error ? e.message : String(e) })
return undefined
})
if (!result) return

status[key] = result.status
Expand Down Expand Up @@ -244,7 +247,7 @@ export namespace MCP {

async function fetchResourcesForClient(clientName: string, client: Client) {
const resources = await withTimeout(client.listResources(), DEFAULT_TIMEOUT).catch((e) => {
log.error("failed to get prompts", { clientName, error: e.message })
log.error("failed to get resources", { clientName, error: e.message })
return undefined
})

Expand Down Expand Up @@ -380,7 +383,7 @@ export namespace MCP {
})
// Census: collect tool and resource counts (fire-and-forget, never block connect)
const remoteTransport = name === "SSE" ? "sse" as const : "streamable-http" as const
Promise.all([
void Promise.all([
client.listTools().catch(() => ({ tools: [] })),
client.listResources().catch(() => ({ resources: [] })),
]).then(([toolsList, resourcesList]) => {
Expand Down Expand Up @@ -496,7 +499,7 @@ export namespace MCP {
duration_ms: Date.now() - localConnectStart,
})
// Census: collect tool and resource counts (fire-and-forget, never block connect)
Promise.all([
void Promise.all([
client.listTools().catch(() => ({ tools: [] })),
client.listResources().catch(() => ({ resources: [] })),
]).then(([toolsList, resourcesList]) => {
Expand Down Expand Up @@ -781,7 +784,7 @@ export namespace MCP {
const client = clientsSnapshot[clientName]

if (!client) {
log.warn("client not found for prompt", {
log.warn("client not found for resource", {
clientName: clientName,
})
return undefined
Expand All @@ -792,7 +795,7 @@ export namespace MCP {
uri: resourceUri,
})
.catch((e) => {
log.error("failed to get prompt from MCP server", {
log.error("failed to read resource from MCP server", {
clientName: clientName,
resourceUri: resourceUri,
error: e.message,
Expand Down
3 changes: 3 additions & 0 deletions packages/altimate-code/src/session/compaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ export namespace SessionCompaction {
}) {
const attempt = (compactionAttempts.get(input.sessionID) ?? 0) + 1
compactionAttempts.set(input.sessionID, attempt)
input.abort.addEventListener("abort", () => {
compactionAttempts.delete(input.sessionID)
}, { once: true })
Telemetry.track({
type: "compaction_triggered",
timestamp: Date.now(),
Expand Down
8 changes: 7 additions & 1 deletion packages/altimate-code/src/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,13 @@ export namespace Telemetry {
log.debug("telemetry flush failed", { status: response.status })
}
} catch {
// Silently drop on failure — telemetry must never break the CLI
// Re-add events that haven't been retried yet to avoid data loss
const retriable = events.filter((e) => !(e as any)._retried)
for (const e of retriable) {
;(e as any)._retried = true
}
const space = Math.max(0, MAX_BUFFER_SIZE - buffer.length)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: The _retried flag is mutated on the original event objects via as any. This works but is a bit fragile — if events are ever cloned or serialized/deserialized between flush attempts, the flag would be lost. For this use case it's fine since events stay in-memory, but worth noting if the architecture ever changes.

buffer.unshift(...retriable.slice(0, space))
} finally {
clearTimeout(timeout)
}
Expand Down
Loading