Skip to content

Commit 595e62e

Browse files
authored
v0.9.23: project files foundation, ci fixes
2 parents ac78fa8 + ce76f14 commit 595e62e

230 files changed

Lines changed: 48865 additions & 3115 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/rules/sim-content-delivery.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ own unless noted.
7777

7878
| Path | U | E | R / C |
7979
|---|---|---|---|
80-
| File download (CLI `files get`) | Delivered once; invocation withheld from the model; workbench copy stays unknown | Redacted | Delivered |
80+
| File download (CLI `files get`) | Delivered once; invocation withheld from the model; workbench copy stays unknown | Opaque: into a chat workbench with entries imported, so output is redacted; refused without one | Delivered |
8181
| File download (run_code proxy) | Refused before bytes enter workbench history | Recorded in workbench history | Delivered |
8282
| Requested mounts | Refused before code executes, listed in `excludedInputs`; later calls remain usable | Recorded in workbench history | Delivered |
8383
| File text, version text, `files read`, grep | 403 | Redacted (grep redacts before clipping) | Delivered |

‎.cursor/rules/sim-content-delivery.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ own unless noted.
6868

6969
| Path | U | E | R / C |
7070
|---|---|---|---|
71-
| File download (CLI `files get`) | Delivered once; invocation withheld from the model; workbench copy stays unknown | Redacted | Delivered |
71+
| File download (CLI `files get`) | Delivered once; invocation withheld from the model; workbench copy stays unknown | Opaque: into a chat workbench with entries imported, so output is redacted; refused without one | Delivered |
7272
| File download (run_code proxy) | Refused before bytes enter workbench history | Recorded in workbench history | Delivered |
7373
| Requested mounts | Refused before code executes, listed in `excludedInputs`; later calls remain usable | Recorded in workbench history | Delivered |
7474
| File text, version text, `files read`, grep | 403 | Redacted (grep redacts before clipping) | Delivered |

‎.github/scripts/promote-app-image.sh‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ REGISTRY="${1:?registry required}"
55
REPOSITORY="${2:?repository required}"
66
SOURCE_TAG="${3:?source tag required}"
77
DEPLOY_TAG="${4:?deploy tag required}"
8+
RELEASE_TAG="${5:-}"
89
: "${GITHUB_OUTPUT:?GitHub output file required}"
910
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
1011
PREVIOUS=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG" --allow-missing)
1112
EPOCH=$(date +%s)
12-
docker buildx imagetools create -t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG" "$REGISTRY/$REPOSITORY:$SOURCE_TAG"
13+
bash "$SCRIPT_DIR/promote-image.sh" "$REGISTRY" "$REPOSITORY" "$SOURCE_TAG" "$DEPLOY_TAG" "$RELEASE_TAG"
1314
DIGEST=$(bash "$SCRIPT_DIR/get-ecr-image-digest.sh" "$REPOSITORY" "$DEPLOY_TAG")
1415
CHANGED=true
1516
if [ "$DIGEST" = "$PREVIOUS" ]; then CHANGED=false; fi

‎.github/scripts/promote-image.sh‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
# Point a deploy tag (and, for production, its release-<sha> alias) at a built image.
3+
# One imagetools call writes both tags, so they always share one list digest.
4+
set -euo pipefail
5+
REGISTRY="${1:?registry required}"
6+
REPOSITORY="${2:?repository required}"
7+
SOURCE_TAG="${3:?source tag required}"
8+
DEPLOY_TAG="${4:?deploy tag required}"
9+
RELEASE_TAG="${5:-}"
10+
TAG_ARGS=(-t "$REGISTRY/$REPOSITORY:$DEPLOY_TAG")
11+
if [ -n "$RELEASE_TAG" ]; then TAG_ARGS+=(-t "$REGISTRY/$REPOSITORY:$RELEASE_TAG"); fi
12+
docker buildx imagetools create "${TAG_ARGS[@]}" "$REGISTRY/$REPOSITORY:$SOURCE_TAG"

‎.github/workflows/checks.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,17 @@ jobs:
243243
working-directory: packages/db
244244
run: bun run db:migrate
245245

246+
# JOBS=4 caps node-gyp parallelism as docker/app.Dockerfile does (laverdet/isolated-vm#428).
247+
- name: Build native isolate addon for Project file rendering
248+
if: matrix.group == 'project-files'
249+
working-directory: apps/sim
250+
env:
251+
JOBS: 4
252+
run: |
253+
addon_dir="$(node -p 'require("node:path").dirname(require.resolve("isolated-vm/package.json", { paths: ["."] }))')"
254+
cd "$addon_dir"
255+
bun run install
256+
246257
# No step timeout: the job's bound covers a hang without cutting a slow but healthy suite
247258
# short of writing its report.
248259
- name: Run end-to-end suites

‎.github/workflows/ci.yml‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,11 @@ jobs:
520520
run: |
521521
REGISTRY="${{ steps.login-ecr.outputs.registry }}"
522522
523+
RELEASE_TAG=""
523524
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
524525
ECR_TAG="latest"
526+
# ECR lifecycle retains release- aliases as prod rollback targets.
527+
RELEASE_TAG="release-${SOURCE_TAG}"
525528
elif [ "${{ github.ref }}" = "refs/heads/dev" ]; then
526529
ECR_TAG="dev"
527530
else
@@ -540,13 +543,11 @@ jobs:
540543
# Move the app last so a preceding tag failure cannot start app rollout
541544
# and then skip the downstream Trigger promotion job.
542545
for repo in $ECR_REPOS; do
543-
echo "🚀 Promoting ${repo}:${SOURCE_TAG} to ${ECR_TAG}"
546+
echo "🚀 Promoting ${repo}:${SOURCE_TAG} to ${ECR_TAG}${RELEASE_TAG:+ and ${RELEASE_TAG}}"
544547
if [ "$repo" = "$APP_REPO" ]; then
545-
bash .github/scripts/promote-app-image.sh "$REGISTRY" "$APP_REPO" "$SOURCE_TAG" "$ECR_TAG"
548+
bash .github/scripts/promote-app-image.sh "$REGISTRY" "$APP_REPO" "$SOURCE_TAG" "$ECR_TAG" "$RELEASE_TAG"
546549
else
547-
docker buildx imagetools create \
548-
-t "${REGISTRY}/${repo}:${ECR_TAG}" \
549-
"${REGISTRY}/${repo}:${SOURCE_TAG}"
550+
bash .github/scripts/promote-image.sh "$REGISTRY" "$repo" "$SOURCE_TAG" "$ECR_TAG" "$RELEASE_TAG"
550551
fi
551552
done
552553

‎apps/desktop/e2e/chat-panel.spec.ts‎

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ test('chat panel sizes survive navigation, chat switches, collapse, and layout c
113113
})
114114
const shell = app
115115
const page = await shell.firstWindow()
116+
const inputSession = await page.context().newCDPSession(page)
117+
// Playwright otherwise keeps the document focused when its native window loses focus.
118+
await inputSession.send('Emulation.setFocusEmulationEnabled', { enabled: false })
116119
page.on('pageerror', (error) => errors.push(error.message))
117120
page.on('crash', () => {
118121
rendererCrashed = true
@@ -253,41 +256,102 @@ test('chat panel sizes survive navigation, chat switches, collapse, and layout c
253256
'detach',
254257
'chat switch',
255258
] as const) {
256-
await check(`${interruption} keeps the previous saved width`, async () => {
259+
const keepsDraggedWidth = interruption === 'capture loss' || interruption === 'blur'
260+
await check(
261+
`${interruption} keeps the ${keepsDraggedWidth ? 'dragged' : 'previous saved'} width`,
262+
async () => {
263+
const before = await page.evaluate(() => JSON.stringify(localStorage))
264+
const rect = await beginDrag()
265+
await page.mouse.move(rect.x + 100, rect.y + 100, { steps: 12 })
266+
await expectWidth(598)
267+
if (interruption === 'pointercancel') {
268+
await divider.dispatchEvent('pointercancel', { pointerId: 1 })
269+
} else if (interruption === 'capture loss') {
270+
await divider.evaluate((element) => element.releasePointerCapture(1))
271+
await page.mouse.move(rect.x + 100, rect.y + 100)
272+
} else if (interruption === 'blur') {
273+
await shell.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].blur())
274+
await expect.poll(() => page.evaluate(() => document.hasFocus())).toBe(false)
275+
} else if (interruption === 'chat switch') {
276+
await page
277+
.getByRole('button', { name: 'organization-chat-a', exact: true })
278+
.evaluate((element: HTMLButtonElement) => element.click())
279+
await expectWidth(560)
280+
} else {
281+
await page
282+
.getByRole('button', { name: 'Settings', exact: true })
283+
.evaluate((element: HTMLButtonElement) => element.click())
284+
await expect(panel).toHaveCount(0)
285+
}
286+
if (keepsDraggedWidth) {
287+
await expectWidth(598)
288+
expect(await page.evaluate(() => JSON.stringify(localStorage))).not.toBe(before)
289+
if (interruption === 'blur') {
290+
await shell.evaluate(({ app, BrowserWindow }) => {
291+
app.focus({ steal: true })
292+
BrowserWindow.getAllWindows()[0].focus()
293+
})
294+
}
295+
await page.mouse.move(rect.x + 150, rect.y + 100)
296+
await expectWidth(598)
297+
}
298+
await page.mouse.up()
299+
if (interruption === 'chat switch') {
300+
await page.getByRole('button', { name: 'workspace-chat-b', exact: true }).click()
301+
}
302+
if (interruption === 'detach') {
303+
await page.getByRole('button', { name: 'Back', exact: true }).click()
304+
}
305+
await expectWidth(keepsDraggedWidth ? 598 : 698)
306+
if (keepsDraggedWidth) {
307+
await page.reload()
308+
await page.getByRole('button', { name: 'workspace-chat-b', exact: true }).click()
309+
await expectWidth(598)
310+
await dragTo(698)
311+
} else {
312+
expect(await page.evaluate(() => JSON.stringify(localStorage))).toBe(before)
313+
}
314+
}
315+
)
316+
}
317+
318+
for (const button of ['middle', 'right'] as const) {
319+
await check(`${button}-button drags leave the panel width unchanged`, async () => {
257320
const before = await page.evaluate(() => JSON.stringify(localStorage))
258-
const rect = await beginDrag()
321+
await divider.hover({ position: { x: 4, y: 100 } })
322+
const rect = await panel.boundingBox()
323+
if (!rect) throw new Error('Missing panel bounds')
324+
await page.mouse.down({ button })
259325
await page.mouse.move(rect.x + 100, rect.y + 100, { steps: 12 })
260-
await expectWidth(598)
261-
if (interruption === 'pointercancel') {
262-
await divider.dispatchEvent('pointercancel', { pointerId: 1 })
263-
} else if (interruption === 'capture loss') {
264-
await divider.evaluate((element) => element.releasePointerCapture(1))
265-
await page.mouse.move(rect.x + 101, rect.y + 100)
266-
} else if (interruption === 'blur') {
267-
await page.evaluate(() => window.dispatchEvent(new Event('blur')))
268-
} else if (interruption === 'chat switch') {
269-
await page
270-
.getByRole('button', { name: 'organization-chat-a', exact: true })
271-
.evaluate((element: HTMLButtonElement) => element.click())
272-
await expectWidth(560)
273-
} else {
274-
await page
275-
.getByRole('button', { name: 'Settings', exact: true })
276-
.evaluate((element: HTMLButtonElement) => element.click())
277-
await expect(panel).toHaveCount(0)
278-
}
279-
await page.mouse.up()
280-
if (interruption === 'chat switch') {
281-
await page.getByRole('button', { name: 'workspace-chat-b', exact: true }).click()
282-
}
283-
if (interruption === 'detach') {
284-
await page.getByRole('button', { name: 'Back', exact: true }).click()
285-
}
326+
await page.mouse.up({ button })
286327
await expectWidth(698)
287328
expect(await page.evaluate(() => JSON.stringify(localStorage))).toBe(before)
288329
})
289330
}
290331

332+
await check('a touch drag resizes the panel without becoming a scroll gesture', async () => {
333+
const rect = await panel.boundingBox()
334+
if (!rect) throw new Error('Missing panel bounds')
335+
await inputSession.send('Emulation.setTouchEmulationEnabled', { enabled: true })
336+
await inputSession.send('Input.dispatchTouchEvent', {
337+
type: 'touchStart',
338+
touchPoints: [{ x: rect.x, y: rect.y + 100 }],
339+
})
340+
for (let offset = 10; offset <= 100; offset += 10) {
341+
await inputSession.send('Input.dispatchTouchEvent', {
342+
type: 'touchMove',
343+
touchPoints: [{ x: rect.x + offset, y: rect.y + 100 }],
344+
})
345+
}
346+
await inputSession.send('Input.dispatchTouchEvent', { type: 'touchEnd', touchPoints: [] })
347+
await inputSession.send('Emulation.setTouchEmulationEnabled', { enabled: false })
348+
await expectWidth(598)
349+
await page.reload()
350+
await page.getByRole('button', { name: 'workspace-chat-b', exact: true }).click()
351+
await expectWidth(598)
352+
await dragTo(698)
353+
})
354+
291355
await check('a viewport change during a drag clamps the committed display width', async () => {
292356
await expectWidth(698)
293357
const rect = await beginDrag()

‎apps/docs/content/docs/cli/files.mdx‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,27 @@ sim files mkdir <path>
802802

803803
API request and response schemas: [createFileFolder](/api-reference/files/createFilesFolder).
804804

805+
## Other commands
806+
807+
### Copy file items
808+
809+
OAuth login or personal API key required.
810+
811+
```bash
812+
sim files copy [options]
813+
```
814+
815+
**Options**
816+
817+
<CommandTable>
818+
819+
| Option | Required | Description |
820+
| --- | --- | --- |
821+
| `--source <json\|@file>` | Yes | Source owner and selected fileIds or folderIds (JSON, or @path / @- to read a file or stdin). |
822+
| `--destination <json\|@file>` | Yes | Destination owner and optional folderId (JSON, or @path / @- to read a file or stdin). |
823+
824+
</CommandTable>
825+
805826
## Related docs
806827

807828
- [Files](/files)

‎apps/docs/content/docs/cli/reference.mdx‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,25 @@ sim files batch-delete [options]
988988

989989
API request and response schemas: [bulkDeleteFiles](/api-reference/files/bulkDeleteFiles).
990990

991+
### sim files copy
992+
993+
Copy file items (OAuth login or personal API key required)
994+
995+
```bash
996+
sim files copy [options]
997+
```
998+
999+
**Options**
1000+
1001+
<CommandTable>
1002+
1003+
| Option | Required | Description |
1004+
| --- | --- | --- |
1005+
| `--source <json\|@file>` | Yes | Source owner and selected fileIds or folderIds (JSON, or @path / @- to read a file or stdin). |
1006+
| `--destination <json\|@file>` | Yes | Destination owner and optional folderId (JSON, or @path / @- to read a file or stdin). |
1007+
1008+
</CommandTable>
1009+
9911010
### sim files create
9921011

9931012
Create file

‎apps/docs/lib/llms.ts‎

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,6 @@ import { getIntegrationLandingPage, INTEGRATION_LANDING_LABEL } from '@/lib/inte
44
import { getApiSpecContent } from '@/lib/openapi'
55
import type { PageData, source } from '@/lib/source'
66

7-
const TABLE_ROW = /^\|.*\|$/
8-
const TABLE_DELIMITER_CELL = /^\s*(:?)-+(:?)\s*$/
9-
const UNESCAPED_PIPE = /(?<!\\)\|/
10-
11-
/**
12-
* The processed-markdown serializer pads every table cell to its column's widest cell, so one long
13-
* enum (a parameter listing every timezone) pads every row of its table and roughly doubles the
14-
* integration reference. Drop that trailing padding; a cell's own leading indentation (the `↳`
15-
* nesting in output tables) and inline code are kept, and the tables stay valid GFM.
16-
*/
17-
function compactTables(markdown: string): string {
18-
let inFence = false
19-
return markdown
20-
.split('\n')
21-
.map((line) => {
22-
if (line.startsWith('```')) inFence = !inFence
23-
if (inFence || !TABLE_ROW.test(line)) return line
24-
const cells = line.slice(1, -1).split(UNESCAPED_PIPE)
25-
if (cells.every((cell) => TABLE_DELIMITER_CELL.test(cell))) {
26-
return `|${cells.map((cell) => cell.replace(TABLE_DELIMITER_CELL, ' $1---$2 ')).join('|')}|`
27-
}
28-
return `|${cells.map((cell) => ` ${cell.replace(/^ /, '').trimEnd()} `).join('|')}|`
29-
})
30-
.join('\n')
31-
}
32-
337
type LLMPageData = PageData & { getAPIPageProps?: () => ApiPageProps }
348

359
/**
@@ -62,5 +36,5 @@ export async function getLLMText(page: InferPageType<typeof source>) {
6236
: ''
6337
return `${heading}
6438
65-
${landingLink}${compactTables(processed)}`
39+
${landingLink}${processed}`
6640
}

0 commit comments

Comments
 (0)