@somewhere-tech/cli 0.37.6 → 0.37.7
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/dist/commands/browser.js +100 -17
- package/dist/commands/browser.js.map +1 -1
- package/dist/commands/call.js +46 -2
- package/dist/commands/call.js.map +1 -1
- package/dist/commands/cron.js +30 -8
- package/dist/commands/cron.js.map +1 -1
- package/dist/commands/deploy.js +5 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +70 -2
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs.js +184 -21
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/env.js +1 -1
- package/dist/commands/promote.js +4 -1
- package/dist/commands/promote.js.map +1 -1
- package/dist/commands/update.js +13 -6
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/verify.js +34 -6
- package/dist/commands/verify.js.map +1 -1
- package/dist/lib/notify/index.js +19 -7
- package/dist/lib/notify/index.js.map +1 -1
- package/dist/lib/notify/providers/update.js +69 -0
- package/dist/lib/notify/providers/update.js.map +1 -1
- package/dist/lib/skills-pack.generated.js +12 -12
- package/dist/lib/skills-pack.generated.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
export const BUNDLED_SKILLS_PACK = {
|
|
2
2
|
"schema": 1,
|
|
3
3
|
"name": "somewhere-skills",
|
|
4
|
-
"version": "1+
|
|
5
|
-
"sha256": "
|
|
4
|
+
"version": "1+46ccaa5adb17",
|
|
5
|
+
"sha256": "46ccaa5adb176861262dd34f446fb2e4c0b3d900ce4fc72a895ee14d002e4467",
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"path": "deploy-verify-loop/SKILL.md",
|
|
9
|
-
"sha256": "
|
|
10
|
-
"content": "---\nname: deploy-verify-loop\ndescription: After every change: deploy the raw source, check the live app, and read logs or errors before guessing at a fix.\n---\n\n# Deploy, verify, read the failure\n\n## The loop\n\
|
|
9
|
+
"sha256": "e90d0f4861b160ccde0fb9e7179cfbac2fb0c9ad2696323c1d11a086cca2a4f7",
|
|
10
|
+
"content": "---\nname: deploy-verify-loop\ndescription: After every change: deploy the raw source, check the live app, and read logs or errors before guessing at a fix.\n---\n\n# Deploy, verify, read the failure\n\n## The loop\n\n```bash\nsomewhere typecheck\nsomewhere deploy\n```\n\nDon't bundle the source yourself; the platform runs the frontend build.\n\n```bash\nsomewhere deploy-check --run /api/hello -X POST -d '{\"name\":\"Ada\"}'\n```\n\n| Step | Command | What it tells you |\n|---|---|---|\n| Verify | `somewhere verify [--flow flow.json]` | Runs the live app at desktop and phone sizes: named steps, console/network health, both screenshots. |\n| Inspect | `somewhere browser [target]` | One page: screenshot, snapshot, DOM, network, or an evaluated expression. |\n| Diagnose | `somewhere logs --tail 10`, `somewhere errors` | Recent function output, then failures split into refused and exception. `errors` needs a claimed project and a signed-in account. |\n\n## A flow for verify --flow\n\n```json\n{ \"actions\": [{ \"fill\": \"#title\", \"value\": \"Verify entry\" }, { \"fill\": \"#from_email\", \"value\": \"v@example.com\" },\n { \"click\": \"#send\" }, { \"expect\": { \"selector\": \"#status\", \"text\": \"Received\" } }], \"viewports\": [\"desktop\", \"mobile\"] }\n```\n\n`passed` requires successful steps and request expectations, no page errors, and no unexpected failed requests. It can be false even when every step succeeded.\n\n## Failure 1: the live site still shows the old version\n\nBrowser or CDN cache. Hard-reload (Cmd-Shift-R), or check with `curl -sI https://{subdomain}.somewhere.site`.\n\n## Failure 2: a route fails\n\nShow recent failures with a `kind` column: **refused** for a 4xx your own handler returned on purpose, **exception** for an uncaught throw or a 5xx.\n\nThe `stack` field is the single most useful field for debugging — it tells you the exact file + line in your code where the throw originated.\n\n| `somewhere rollback [project]` | `-y, --yes`, `--json` | Select the previous retained production release. |\n\nSources (`somewhere docs <topic>`): browser, cli, common-mistakes, getting-started, recipe-signed-in-app, troubleshooting.\n"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"path": "schema-changes/SKILL.md",
|
|
14
|
-
"sha256": "
|
|
15
|
-
"content": "---\nname: schema-changes\ndescription: Before adding, changing, or removing a table or column in db/schema.ts.\n---\n\n# Change the database schema safely\n\n## The file is the contract\n\n```ts\n// db/schema.ts
|
|
14
|
+
"sha256": "18380e80cec6c77352c328fa8b58cf3011d3dd90bd23711d38d3b36d50bd3304",
|
|
15
|
+
"content": "---\nname: schema-changes\ndescription: Before adding, changing, or removing a table or column in db/schema.ts.\n---\n\n# Change the database schema safely\n\n## The file is the contract\n\n```ts\n// db/schema.ts\nimport { id, owner, schema, table, text, timestamp } from 'somewhere/db';\n\nexport default schema({\n notes: table(\n {\n id: id(),\n title: text(),\n body: text({ default: '' }),\n created_at: timestamp({ default: 'now' }),\n },\n {\n scope: owner(),\n client: {\n read: ['id', 'title', 'body', 'created_at'],\n create: ['title', 'body'],\n update: ['title', 'body'],\n delete: true,\n },\n },\n ),\n});\n```\n\nA table without a `client` block can't be reached from the browser at all.\n\nDeclare a file collection in `db/schema.ts`: who may upload, read, replace and delete its files.\n\n| `somewhere typecheck [dir]` | `--json` | Run local `tsc --noEmit` over a pulled project. |\n\n## What a deploy does with it\n\n- **Additive changes apply**: a new table, a new nullable-or-defaulted column, a new index, a new unique group. Unchanged schema is a no-op.\n- **Unmarked removals and shape changes refuse** with `409 SCHEMA_DEPLOY_REFUSED` and a message naming the change.\n- **Removing a table** is `old_table: removedTable()`.\n- once the new version is live it retires the table and keeps its data for 30 days.\n- a production deploy or a promotion that would remove or rename a column is refused before anything changes, so a running release never loses a column it uses. Keep the column declared and leave its data in place.\n\n## Failure: raw SQL refused after the schema file deploys\n\nOrdinary `sw.db.query` / `sw.db.batch` fail before transport with `MANAGED_RAW_SQL_REQUIRES_SERVER_AUTHORITY`, including in job, queue, and cron handlers. Fix: write with `sw.db.insert` / `sw.db.update` / `sw.db.remove` (the table needs a declared intent)\n\nSources (`somewhere docs <topic>`): cli, declared-data, declared-files, sw.db, troubleshooting.\n"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": "sign-in-setup/SKILL.md",
|
|
19
|
-
"sha256": "
|
|
20
|
-
"content": "---\nname: sign-in-setup\ndescription: When the app needs users to sign up, sign in, or sign out.\n---\n\n# Set up sign-in\n\n## Server: one route file\n\n`login` / `signup` are developer-key-gated (the browser can't call them), so one server route mediates them and sets the session cookies.\n\n```ts\n// api/auth/[...path].ts\nexport { somewhereAuth as default } from '@somewhere-tech/sdk/server';\n```\n\nIt serves `POST /signup`, `/login`, `/logout`, `/magic-link`, `/magic-link/verify`, `GET /me`, and the Google/GitHub/Discord `GET /<provider>-url` + `POST /<provider>` code exchange.\n\n## Page: the SDK client\n\n```bash\nnpm i @somewhere-tech/sdk\n```\n\n```ts\nimport { createSomewhereAuth } from '@somewhere-tech/sdk/auth'
|
|
19
|
+
"sha256": "1ab5b6e50ed76058ac2cdf6507da0ddc59eb81d37d7245c9c95307935c9fa61e",
|
|
20
|
+
"content": "---\nname: sign-in-setup\ndescription: When the app needs users to sign up, sign in, or sign out.\n---\n\n# Set up sign-in\n\n## Server: one route file\n\n`login` / `signup` are developer-key-gated (the browser can't call them), so one server route mediates them and sets the session cookies.\n\n```ts\n// api/auth/[...path].ts\nexport { somewhereAuth as default } from '@somewhere-tech/sdk/server';\n```\n\nIt serves `POST /signup`, `/login`, `/logout`, `/magic-link`, `/magic-link/verify`, `GET /me`, and the Google/GitHub/Discord `GET /<provider>-url` + `POST /<provider>` code exchange.\n\n## Page: the SDK client\n\n```bash\nnpm i @somewhere-tech/sdk\n```\n\n```ts\n// src/auth.ts — page code\nimport { createSomewhereAuth } from '@somewhere-tech/sdk/auth'\n\nexport const auth = createSomewhereAuth()\n\n// Call from your sign-up form; throws AuthError with the message on failure.\nexport async function signUp(email: string, password: string) {\n await auth.signUp({ email, password }) // or auth.signIn({ email, password })\n return auth.getUser() // the signed-in user, or null\n}\nexport const signOut = () => auth.signOut()\n```\n\nCalls to your own `api/*` functions just send `credentials: 'include'`.\n\nDon't write `getCookie()` / token-parsing helpers → `sw.auth.fromRequest(req)` reads cookie + Bearer header and returns the validated user (or null) in one call.\n\nMail to `<name>@<project>.test.somewhere.site` is read with `somewhere email test-inbox <address>`.\n\n## Failure 1: sign-in errors\n\nexpected failures (wrong password, duplicate email, weak password) come back as structured 4xx with the real message, never an opaque 500.\n\n## Failure 2: AUTH_REQUIRED from an owner() table\n\nThe table is user-owned, so the platform scopes it to a principal, and this request had none: there is no signed-in user, and this table does not declare `owner({ visitors: true })`, which is the declaration that accepts a stable anonymous visitor identity.\n\nSources (`somewhere docs <topic>`): auth-client, common-mistakes, recipe-signed-in-app, sdk, troubleshooting.\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "troubleshooting/SKILL.md",
|
|
24
|
-
"sha256": "
|
|
25
|
-
"content": "---\nname: troubleshooting\ndescription: When a request fails, a deploy or save is refused, or the live app misbehaves and the cause is not obvious.\n---\n\n# Troubleshoot a failure\n\n## Read before guessing\n\
|
|
24
|
+
"sha256": "f8ccf9611284085d8987cd8d7a8f53e7be90049ab12771e44605c35d8397c8ce",
|
|
25
|
+
"content": "---\nname: troubleshooting\ndescription: When a request fails, a deploy or save is refused, or the live app misbehaves and the cause is not obvious.\n---\n\n# Troubleshoot a failure\n\n## Read before guessing\n\n```bash\nsomewhere logs --tail 20 # recent log lines\nsomewhere logs --level error --since 1h # only errors from the last hour\nsomewhere logs --function /api/account # one route\nsomewhere errors # recent runtime failures with stack and path\n```\n\nRead the failure first, then change code.\n\nEvery JSON error from the platform now ships with a `hint` field that names the most relevant `docs` topic.\n\n## Limits the logs name\n\n| Ceiling | Value | Instead |\n|---|---|---|\n| Memory per request | about 256 MB | Stream or process in chunks; move heavy work to `sw.jobs` |\n| CPU time per request | about 30 s | Move long computation to `sw.jobs` |\n| Wall-clock time per request | about 300 s | Start the work with `sw.jobs` and return; poll the job |\n\n## Failure 1: FUNCTION_CPU_EXCEEDED or FUNCTION_MEMORY_EXCEEDED\n\nThe caller gets HTTP 500 in the platform's JSON error envelope, `{ ok: false, error, message, request_id }`, with `error` set to `FUNCTION_MEMORY_EXCEEDED` or `FUNCTION_CPU_EXCEEDED`, and the same code and message are recorded in the project's logs and errors. The fix is the \"Instead\" column: move the work to `sw.jobs`, or process it in smaller pieces.\n\n## Failure 2: a refused deploy\n\n`SCHEMA_DEPLOY_REFUSED` (409, at deploy) — `db/schema.ts` asked for something the deploy will not do silently\n\nDoes the file path match the URL? `api/users/[id].ts` serves `/api/users/123`. `api/users.ts` serves `/api/users`.\n\nSources (`somewhere docs <topic>`): browser, limits, sw.db, troubleshooting.\n"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "realtime-and-background/SKILL.md",
|
|
29
|
-
"sha256": "
|
|
30
|
-
"content": "---\nname: realtime-and-background\ndescription: When the page must update without a reload, users need audio/video, or work must run outside one request.\n---\n\n# Live views, call rooms, jobs, queues, cron\n\n## Live view: the page updates when rows change\n\n`sw.db.live(name, sw.db.from(...))` is the live path a signed-in browser can subscribe to.\n\n```ts\n// api/open-notes.ts — the declaration and the SELECT stay server-side\nexport default async function (_req, sw) {\n return Response.json(await sw.db.live('notes.open', sw.db.from('notes', {\n where: { done: false },\n columns: ['id', 'title', 'done', 'created_at'],\n order: [['created_at', 'desc'], ['id', 'asc']],\n limit: 20,\n })))\n}\n\n// browser — the loader re-calls the function, never the database\nimport { watchLive } from '/__sw/live/client.js'\n\nconst stop = watchLive(\n () => fetch('/api/open-notes', { credentials: 'include' }).then(r => r.json()),\n (rows) => render(rows),\n)\n```\n\n## Call room: audio and video\n\nBuild video calling, screen sharing, or live audio rooms.\n\n## Job: slow work with a status\n\nQueue work that shouldn't block a user request. Make handlers idempotent — they may run more than once.\n\n```ts\nconst job = await sw.jobs.create({\n handler: '/api/jobs/
|
|
29
|
+
"sha256": "ffa18344dae04bb1cefb78d5b8bb0a3911fcea553f8db1aa6179935a4573bd8c",
|
|
30
|
+
"content": "---\nname: realtime-and-background\ndescription: When the page must update without a reload, users need audio/video, or work must run outside one request.\n---\n\n# Live views, call rooms, jobs, queues, cron\n\n## Live view: the page updates when rows change\n\n`sw.db.live(name, sw.db.from(...))` is the live path a signed-in browser can subscribe to.\n\n```ts\n// api/open-notes.ts — the declaration and the SELECT stay server-side\nexport default async function (_req, sw) {\n return Response.json(await sw.db.live('notes.open', sw.db.from('notes', {\n where: { done: false },\n columns: ['id', 'title', 'done', 'created_at'],\n order: [['created_at', 'desc'], ['id', 'asc']],\n limit: 20,\n })))\n}\n\n// browser — the loader re-calls the function, never the database\nimport { watchLive } from '/__sw/live/client.js'\n\nconst stop = watchLive(\n () => fetch('/api/open-notes', { credentials: 'include' }).then(r => r.json()),\n (rows) => render(rows),\n)\n```\n\n## Call room: audio and video\n\nBuild video calling, screen sharing, or live audio rooms.\n\n## Job: slow work with a status\n\nQueue work that shouldn't block a user request. Make handlers idempotent — they may run more than once.\n\n```ts\nconst job = await sw.jobs.create({\n handler: '/api/jobs/summarize-notes',\n idempotency_key: `summary:${user.id}:${operationId}`,\n // user_id comes from the verified session, so the handler can trust it.\n payload: { summary_id: `${user.id}:${operationId}`, user_id: user.id },\n});\n```\n\n```ts\nif (!(await sw.jobs.verifyInvocation(req))) {\n return Response.json({ error: 'forbidden' }, { status: 403 })\n}\n```\n\n## Queue: fire and forget\n\nFor fire-and-forget tasks where you don't need to inspect status later. Use sw.jobs if you DO need to look up the result.\n\n## Cron: on a schedule\n\nSchedules run on Builder and higher. The finest granularity is one minute: a 5-field expression has no seconds field.\n\n```js\ncron_create({\n project_id: \"my-app\",\n schedule: \"0 9 * * *\", // 5-field cron expression\n timezone: \"America/Los_Angeles\", // optional; defaults to UTC\n handler: \"/api/jobs/daily-digest\",\n payload: { segment: \"daily\" } // optional\n})\n```\n\n```bash\nsomewhere cron run cleanup --project <project> --wait # run once now and wait for the result (id or name)\n```\n\n## Failure 1: REALTIME_RETIRED (410) or CHANNEL_FORBIDDEN (403)\n\nCaller-named channels are retired. This is not a configuration problem and there is no flag to turn them on.\n\n## Failure 2: CRON_SCHEDULE_TOO_FREQUENT\n\nA schedule tighter than `min_interval_minutes` is refused when you create or edit it, with `CRON_SCHEDULE_TOO_FREQUENT` naming both intervals.\n\nSources (`somewhere docs <topic>`): calls, cron, live, sw.jobs, sw.queue, troubleshooting.\n"
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"path": "email-and-test-inbox/SKILL.md",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills-pack.generated.js","sourceRoot":"","sources":["../../src/lib/skills-pack.generated.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,kBAAkB;IAC1B,SAAS,EAAE,gBAAgB;IAC3B,QAAQ,EAAE,kEAAkE;IAC5E,OAAO,EAAE;QACP;YACE,MAAM,EAAE,6BAA6B;YACrC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"skills-pack.generated.js","sourceRoot":"","sources":["../../src/lib/skills-pack.generated.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,mBAAmB,GAAe;IAC7C,QAAQ,EAAE,CAAC;IACX,MAAM,EAAE,kBAAkB;IAC1B,SAAS,EAAE,gBAAgB;IAC3B,QAAQ,EAAE,kEAAkE;IAC5E,OAAO,EAAE;QACP;YACE,MAAM,EAAE,6BAA6B;YACrC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,8oEAA8oE;SAC1pE;QACD;YACE,MAAM,EAAE,yBAAyB;YACjC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,ggEAAggE;SAC5gE;QACD;YACE,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,mkEAAmkE;SAC/kE;QACD;YACE,MAAM,EAAE,0BAA0B;YAClC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,gwDAAgwD;SAC5wD;QACD;YACE,MAAM,EAAE,kCAAkC;YAC1C,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,myFAAmyF;SAC/yF;QACD;YACE,MAAM,EAAE,+BAA+B;YACvC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,+5CAA+5C;SAC36C;KACF;CACF,CAAC"}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@somewhere-tech/cli",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@somewhere-tech/cli",
|
|
9
|
-
"version": "0.37.
|
|
9
|
+
"version": "0.37.7",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"@modelcontextprotocol/sdk",
|
|
12
12
|
"chokidar",
|