Merge remote-tracking branch 'origin/master' into fix/runner-paid-matrix-integrity
* origin/master: chore(deps): bump better-auth from 1.7.0 to 1.7.2 (#12565) chore(deps-dev): bump @vitejs/plugin-react from 4.7.0 to 6.1.1 (#12566) chore(lockfile): refresh pnpm-lock.yaml (#12771) chore(deps): bump @aws-sdk/client-s3 from 3.1115.0 to 3.1120.0 (#12567) ci(runner): allow trusted branch targets (#12768) chore(deps): bump @tanstack/react-query from 5.101.4 to 5.102.8 (#12568) chore(deps): bump mermaid from 11.16.1 to 11.17.2 (#12569) # Conflicts: # .github/workflows/runner-full-stack-e2e.yml
This commit is contained in:
commit
0f140a3433
|
|
@ -0,0 +1,24 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
const workflows = [
|
||||
'.github/workflows/refresh-lockfile.yml',
|
||||
'.github/workflows/pr-trusted.yml',
|
||||
'.github/workflows/docker.yml',
|
||||
];
|
||||
|
||||
test('lockfile repair workflows resolve dependencies instead of updating metadata only', async () => {
|
||||
for (const workflow of workflows) {
|
||||
const contents = await readFile(workflow, 'utf8');
|
||||
const repairCommands = contents
|
||||
.split('\n')
|
||||
.filter((line) => line.includes('pnpm install') && line.includes('--no-frozen-lockfile'));
|
||||
|
||||
assert.ok(repairCommands.length > 0, `${workflow} must contain a lockfile repair command`);
|
||||
for (const command of repairCommands) {
|
||||
assert.match(command, /--ignore-scripts/);
|
||||
assert.doesNotMatch(command, /--lockfile-only/);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -87,7 +87,7 @@ jobs:
|
|||
- name: Refresh lockfile for Docker build context
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
|
||||
pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
|
||||
changed="$(git status --porcelain)"
|
||||
if [ -z "$changed" ]; then
|
||||
|
|
@ -281,7 +281,7 @@ jobs:
|
|||
- name: Refresh lockfile for Docker build context
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
|
||||
pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
|
||||
changed="$(git status --porcelain)"
|
||||
if [ -z "$changed" ]; then
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ jobs:
|
|||
id: regen_lockfile
|
||||
run: |
|
||||
cp pnpm-lock.yaml "$RUNNER_TEMP/pnpm-lock.before.yaml"
|
||||
pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
|
||||
pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
if cmp -s "$RUNNER_TEMP/pnpm-lock.before.yaml" pnpm-lock.yaml; then
|
||||
echo "regenerated=0" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
cache: pnpm
|
||||
|
||||
- name: Refresh pnpm lockfile
|
||||
run: pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile
|
||||
run: pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
|
||||
- name: Fail on unexpected file changes
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ on:
|
|||
- cron: "47 8 * * 0"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target_branch:
|
||||
description: "Branch in paperclipai/paperclip to test; the trusted workflow still runs from master"
|
||||
type: string
|
||||
required: false
|
||||
all:
|
||||
description: "Run the complete paid matrix when no narrower selector is supplied"
|
||||
type: boolean
|
||||
|
|
@ -38,10 +42,10 @@ permissions:
|
|||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: runner-full-stack-e2e-${{ github.ref }}
|
||||
# Development-only validation refs supersede older runs on the same ref.
|
||||
# Preserve every protected default-branch campaign for its paid audit trail.
|
||||
cancel-in-progress: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
group: runner-full-stack-e2e-${{ inputs.target_branch || github.event.repository.default_branch }}
|
||||
# Development branch campaigns supersede older runs for the same target.
|
||||
# Preserve every default-branch campaign for its paid audit trail.
|
||||
cancel-in-progress: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch != '' && inputs.target_branch != github.event.repository.default_branch }}
|
||||
|
||||
jobs:
|
||||
authorize:
|
||||
|
|
@ -55,6 +59,7 @@ jobs:
|
|||
test_runner: ${{ steps.runner.outputs.runner }}
|
||||
max_parallel_default: ${{ steps.runner.outputs.max_parallel_default }}
|
||||
max_parallel_limit: ${{ steps.runner.outputs.max_parallel_limit }}
|
||||
target_sha: ${{ steps.target.outputs.sha }}
|
||||
steps:
|
||||
- name: Require default branch and allowlisted numeric actor IDs
|
||||
env:
|
||||
|
|
@ -89,6 +94,27 @@ jobs:
|
|||
fi
|
||||
done
|
||||
|
||||
- name: Resolve requested repository branch to an immutable commit
|
||||
id: target
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
TARGET_BRANCH: ${{ inputs.target_branch || github.event.repository.default_branch }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "$TARGET_BRANCH" ] || [[ "$TARGET_BRANCH" == refs/* ]]; then
|
||||
echo "target_branch must name a branch in this repository without a refs/ prefix." >&2
|
||||
exit 1
|
||||
fi
|
||||
encoded_branch="$(jq -rn --arg branch "$TARGET_BRANCH" '$branch | @uri')"
|
||||
target_sha="$(gh api -X GET "repos/$REPOSITORY/branches/$encoded_branch" --jq .commit.sha)"
|
||||
if ! [[ "$target_sha" =~ ^[0-9a-f]{40}$ ]]; then
|
||||
echo "The requested repository branch did not resolve to a commit." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "sha=$target_sha" >> "$GITHUB_OUTPUT"
|
||||
echo "Resolved the requested repository branch to $target_sha."
|
||||
|
||||
- name: Select paid test runner
|
||||
id: runner
|
||||
env:
|
||||
|
|
@ -133,6 +159,9 @@ jobs:
|
|||
daytona_image_content_id: ${{ steps.daytona_image_content.outputs.content_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.authorize.outputs.target_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
with:
|
||||
|
|
@ -241,6 +270,9 @@ jobs:
|
|||
content_id: ${{ steps.image.outputs.content_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.authorize.outputs.target_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: No Daytona image needed
|
||||
id: local_only
|
||||
|
|
@ -269,6 +301,7 @@ jobs:
|
|||
NEEDS_DAYTONA: ${{ needs.catalog.outputs.needs_daytona }}
|
||||
IMAGE_CONTENT_ID: ${{ needs.catalog.outputs.daytona_image_content_id }}
|
||||
IMAGE_TAG: ghcr.io/paperclipai/paperclip-daytona-runner:e2e-content-${{ needs.catalog.outputs.daytona_image_content_id }}
|
||||
TARGET_SHA: ${{ needs.authorize.outputs.target_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ "$NEEDS_DAYTONA" != true ]; then
|
||||
|
|
@ -285,7 +318,7 @@ jobs:
|
|||
docker buildx build \
|
||||
--platform linux/amd64 \
|
||||
--build-arg "PAPERCLIP_RUNNER_CONTENT_ID=${IMAGE_CONTENT_ID}" \
|
||||
--build-arg "PAPERCLIP_RUNNER_SOURCE_REVISION=${GITHUB_SHA}" \
|
||||
--build-arg "PAPERCLIP_RUNNER_SOURCE_REVISION=${TARGET_SHA}" \
|
||||
--file docker/daytona-runner/Dockerfile \
|
||||
--tag "$IMAGE_TAG" \
|
||||
--push \
|
||||
|
|
@ -558,6 +591,7 @@ jobs:
|
|||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ needs.authorize.outputs.target_sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
|
|
@ -677,7 +711,7 @@ jobs:
|
|||
report:
|
||||
name: Merge and enforce campaign result
|
||||
if: always() && !cancelled() && needs.catalog.result == 'success'
|
||||
needs: [catalog, daytona_image, test]
|
||||
needs: [authorize, catalog, daytona_image, test]
|
||||
outputs:
|
||||
history_source_ready: ${{ steps.history_source_ready.outputs.ready }}
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -686,6 +720,10 @@ jobs:
|
|||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# Reporting and sanitization are part of the trusted workflow boundary.
|
||||
ref: ${{ github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
with:
|
||||
|
|
@ -772,7 +810,7 @@ jobs:
|
|||
|
||||
publish_history:
|
||||
name: Publish pruned immutable history and landing site
|
||||
needs: [catalog, report]
|
||||
needs: [authorize, catalog, report]
|
||||
if: always() && needs.catalog.result == 'success' && needs.report.outputs.history_source_ready == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
|
@ -786,6 +824,10 @@ jobs:
|
|||
name: runner-e2e-history
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
# Never execute target-controlled publication code with AWS credentials.
|
||||
ref: ${{ github.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@
|
|||
"@types/node": "^24.0.0",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"axe-core": "^4.12.1",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
|
|
|
|||
440
pnpm-lock.yaml
440
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -306,8 +306,8 @@ test("the trusted PR workflow regenerates stale stacked lockfiles", () => {
|
|||
);
|
||||
assert.match(
|
||||
workflow,
|
||||
/pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile/,
|
||||
"the policy job must validate the complete merge tree instead of only the current PR layer",
|
||||
/pnpm install --ignore-scripts --no-frozen-lockfile/,
|
||||
"the policy job must resolve the complete merge tree instead of only updating lockfile metadata",
|
||||
);
|
||||
assert.match(
|
||||
workflow,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
"typecheck": "pnpm run prepare:runner-vendor && pnpm --filter @paperclipai/plugin-sdk ensure-build-deps && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.1115.0",
|
||||
"@aws-sdk/client-s3": "^3.1120.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@paperclipai/adapter-claude-local": "workspace:*",
|
||||
"@paperclipai/adapter-codex-local": "workspace:*",
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
"acpx": "0.13.1",
|
||||
"ajv": "^8.20.0",
|
||||
"ajv-formats": "^3.0.1",
|
||||
"better-auth": "1.7.0",
|
||||
"better-auth": "1.7.2",
|
||||
"chokidar": "^5.0.0",
|
||||
"detect-port": "^2.1.0",
|
||||
"dompurify": "^3.4.13",
|
||||
|
|
|
|||
|
|
@ -271,13 +271,35 @@ auto-stop/archive/delete values remain as cancellation backstops.
|
|||
## GitHub Actions
|
||||
|
||||
`Runner Full-Stack E2E` has only `schedule` and `workflow_dispatch` triggers; it
|
||||
never runs for a pull request or ordinary push. Because this repository is
|
||||
public, manual campaigns fail before checkout unless they run from the default
|
||||
branch and both the original actor and rerun actor have numeric GitHub user IDs
|
||||
in the non-empty JSON-array repository variable
|
||||
`RUNNER_E2E_ALLOWED_ACTOR_IDS`. Usernames are intentionally not trusted.
|
||||
The first scheduled attempt is trusted automation; any human rerun of a
|
||||
scheduled campaign must pass the triggering-actor allowlist.
|
||||
never runs for a pull request or ordinary push. Start the trusted workflow from
|
||||
the default branch. A CODEOWNER can set the optional `target_branch` input to
|
||||
any branch in `paperclipai/paperclip`. The authorization job resolves that
|
||||
branch to one immutable commit before any checkout. Catalog, image, and paid
|
||||
test jobs check out that exact commit. Report sanitization and AWS history
|
||||
publication explicitly check out the trusted workflow commit. The workflow
|
||||
definition, runner-group permission, and protected-environment deployment still
|
||||
come from the default branch. Do not select the target branch in GitHub's **Use
|
||||
workflow from** control.
|
||||
|
||||
Because this repository is public, manual campaigns fail before checkout unless
|
||||
the trusted workflow runs from the default branch and both the original actor
|
||||
and rerun actor have numeric GitHub user IDs in the non-empty JSON-array
|
||||
repository variable `RUNNER_E2E_ALLOWED_ACTOR_IDS`. Keep this stable-ID list in
|
||||
sync with the owners of `.github/**` in `.github/CODEOWNERS`. Usernames are
|
||||
intentionally not trusted. The first scheduled attempt is trusted automation;
|
||||
any human rerun of a scheduled campaign must pass the triggering-actor
|
||||
allowlist.
|
||||
|
||||
For example, this command runs one branch cell through the trusted default-branch
|
||||
workflow:
|
||||
|
||||
```bash
|
||||
gh workflow run runner-full-stack-e2e.yml \
|
||||
--ref master \
|
||||
-f target_branch=fix/example \
|
||||
-f all=false \
|
||||
-f id=core-compatibility.runner-codex.local.message-marker
|
||||
```
|
||||
|
||||
Create a protected `runner-e2e-paid` GitHub environment, restrict it to the
|
||||
default branch, limit environment administration to trusted maintainers, and
|
||||
|
|
@ -307,11 +329,14 @@ it, and require a fresh ephemeral instance for each job so one paid cell cannot
|
|||
leave state for the next. Provider secrets remain protected by the stable-ID
|
||||
authorization checks and the default-branch-only `runner-e2e-paid` environment;
|
||||
the fleet itself is not an authorization boundary. These external fleet controls
|
||||
are as important as the workflow checks in a public repository.
|
||||
are as important as the workflow checks in a public repository. A CODEOWNER
|
||||
dispatch is an explicit authorization to execute the selected repository branch
|
||||
with the cell's scoped provider credential.
|
||||
|
||||
Non-default validation runs share a concurrency key per ref and cancel an older
|
||||
run when a replacement is dispatched. Protected default-branch paid campaigns
|
||||
are retained and are never auto-cancelled, preserving their audit trail.
|
||||
Development branch campaigns share a concurrency key per target branch and
|
||||
cancel an older run when a replacement is dispatched. Default-branch target
|
||||
campaigns are retained and are never auto-cancelled, preserving their audit
|
||||
trail.
|
||||
|
||||
GitHub Actions artifacts are access-controlled 30-day operational copies, not
|
||||
the permanent public history. They retain packaged PNG/WebM and generated
|
||||
|
|
|
|||
|
|
@ -9,15 +9,23 @@ changes.
|
|||
## GitHub authorization
|
||||
|
||||
Set `RUNNER_E2E_ALLOWED_ACTOR_IDS` to a non-empty JSON array of numeric GitHub
|
||||
user IDs, for example `[123456,789012]`. Resolve each ID from the authenticated
|
||||
CLI and verify the login before adding it:
|
||||
user IDs. Keep the list equal to the owners of `.github/**` in
|
||||
`.github/CODEOWNERS`. For example, use `[123456,789012]`. Resolve each ID from
|
||||
the authenticated CLI and verify the login before adding it:
|
||||
|
||||
```bash
|
||||
gh api users/LOGIN --jq '{login,id}'
|
||||
```
|
||||
|
||||
The paid workflows reject manual dispatches outside the default branch before
|
||||
checkout. They verify both the original actor and triggering actor for every
|
||||
The paid workflows reject manual dispatches when the workflow definition does
|
||||
not come from the default branch. A trusted dispatcher may name any branch in
|
||||
`paperclipai/paperclip` as the code under test. The authorization job resolves
|
||||
that branch through the GitHub API and passes only its immutable commit SHA to
|
||||
the catalog, image, and paid test checkouts. Report sanitization and AWS history
|
||||
publication explicitly use the trusted workflow commit. Never run the workflow
|
||||
definition from the target branch.
|
||||
|
||||
The workflows verify both the original actor and triggering actor for every
|
||||
scheduled or manual attempt, including human reruns. Every
|
||||
secret-bearing job repeats this check as its first step so GitHub's partial-job
|
||||
rerun feature cannot bypass a successful predecessor authorization job. The
|
||||
|
|
@ -79,14 +87,23 @@ fresh ephemeral instance for every job, prohibit persistent runner reuse, and
|
|||
disable interactive SSH/debug access unless a separate incident procedure
|
||||
explicitly authorizes it.
|
||||
|
||||
Changing the runner does not widen secret access. The paid workflow still has
|
||||
only schedule and manual triggers, requires the protected default branch and
|
||||
allowlisted stable actor IDs before checkout, repeats that authorization as the
|
||||
first matrix step, and receives provider credentials only from the protected
|
||||
Changing the runner does not widen who can authorize secret access. The paid
|
||||
workflow still has only schedule and manual triggers, requires its trusted
|
||||
definition to come from the protected default branch, requires allowlisted
|
||||
stable actor IDs before checkout, and repeats that authorization as the first
|
||||
matrix step. Provider credentials come only from the protected
|
||||
`runner-e2e-paid` environment. The fleet selector is an exact workflow literal;
|
||||
the only repository-controlled input is its boolean rollout switch, so
|
||||
the only repository-controlled routing input is its boolean rollout switch, so
|
||||
configuration cannot redirect a secret-bearing job to an arbitrary runner.
|
||||
|
||||
The optional target branch is code, not workflow authority. A CODEOWNER who
|
||||
dispatches a target branch explicitly authorizes that branch's selected test
|
||||
process to receive the cell's scoped provider credential. The workflow resolves
|
||||
the target only inside the same repository, pins one SHA for the campaign, and
|
||||
checks it out only after authorization. Target-controlled code cannot replace
|
||||
the report sanitizer or the AWS history publisher. Fork refs and
|
||||
target-controlled workflow definitions do not enter this path.
|
||||
|
||||
## AWS OIDC and S3
|
||||
|
||||
The AWS role trust policy should accept only GitHub's OIDC audience and the
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@ describe("public repository paid workflow security", () => {
|
|||
expect(authorizeJob).toContain(
|
||||
"AWS_PAID_RUNNER_ENABLED: ${{ vars.RUNNER_E2E_AWS_ENABLED }}",
|
||||
);
|
||||
expect(authorizeJob).toContain(
|
||||
"Resolve requested repository branch to an immutable commit",
|
||||
);
|
||||
expect(authorizeJob).toContain(
|
||||
"repos/$REPOSITORY/branches/$encoded_branch",
|
||||
);
|
||||
expect(authorizeJob).toContain('echo "sha=$target_sha"');
|
||||
expect(paidJob).toContain(
|
||||
"runs-on: ${{ needs.authorize.outputs.test_runner }}",
|
||||
);
|
||||
|
|
@ -91,7 +98,34 @@ describe("public repository paid workflow security", () => {
|
|||
'[ "$MAX_PARALLEL" -gt "$MAX_PARALLEL_LIMIT" ]',
|
||||
);
|
||||
expect(fullStack).toContain(
|
||||
"cancel-in-progress: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }}",
|
||||
"group: runner-full-stack-e2e-${{ inputs.target_branch || github.event.repository.default_branch }}",
|
||||
);
|
||||
expect(fullStack).toContain(
|
||||
"cancel-in-progress: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch != '' && inputs.target_branch != github.event.repository.default_branch }}",
|
||||
);
|
||||
expect(
|
||||
fullStack.match(
|
||||
/ref: \$\{\{ needs\.authorize\.outputs\.target_sha \}\}/g,
|
||||
),
|
||||
).toHaveLength(3);
|
||||
expect(fullStack.match(/ref: \$\{\{ github\.sha \}\}/g)).toHaveLength(2);
|
||||
expect(fullStack.match(/persist-credentials: false/g)).toHaveLength(5);
|
||||
expect(fullStack).not.toContain("ref: ${{ inputs.target_branch }}");
|
||||
expect(fullStack).toContain(
|
||||
"PAPERCLIP_RUNNER_SOURCE_REVISION=${TARGET_SHA}",
|
||||
);
|
||||
const reportJob = fullStack.slice(
|
||||
fullStack.indexOf(" report:"),
|
||||
fullStack.indexOf(" publish_history:"),
|
||||
);
|
||||
const historyJob = fullStack.slice(fullStack.indexOf(" publish_history:"));
|
||||
expect(reportJob).toContain("ref: ${{ github.sha }}");
|
||||
expect(reportJob).not.toContain(
|
||||
"ref: ${{ needs.authorize.outputs.target_sha }}",
|
||||
);
|
||||
expect(historyJob).toContain("ref: ${{ github.sha }}");
|
||||
expect(historyJob).not.toContain(
|
||||
"ref: ${{ needs.authorize.outputs.target_sha }}",
|
||||
);
|
||||
expect(fullStack).toContain(
|
||||
"if: always() && !cancelled() && needs.catalog.result == 'success'",
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@
|
|||
"@paperclipai/shared": "workspace:*",
|
||||
"@radix-ui/react-slot": "^1.3.0",
|
||||
"@tailwindcss/typography": "^0.5.20",
|
||||
"@tanstack/react-query": "^5.101.4",
|
||||
"@tanstack/react-query": "^5.102.8",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
"i18next": "^26.3.6",
|
||||
"lexical": "0.48.0",
|
||||
"lucide-react": "^1.32.0",
|
||||
"mermaid": "^11.16.1",
|
||||
"mermaid": "^11.17.2",
|
||||
"motion": "^12.42.2",
|
||||
"radix-ui": "^1.6.7",
|
||||
"react": "^19.2.8",
|
||||
|
|
@ -82,7 +82,7 @@
|
|||
"@types/node": "^24.0.0",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"storybook": "10.5.10",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^7.0.2",
|
||||
|
|
|
|||
Loading…
Reference in New Issue