@somewhere-cloud/cli 0.38.1 → 0.38.3

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.
@@ -40,8 +40,8 @@ role, email_verified, … }\` or throws 401 \`AUTH_REQUIRED\`;
40
40
  Functions: JSON endpoints use \`sw.endpoint({ auth, body, rateLimit, handler:
41
41
  async ({ body, user, params }, sw) => value })\`; set \`auth\` (\`'required'\`,
42
42
  \`'optional'\`, \`'none'\`) or deploy refuses it. Webhooks and streaming use a bare \`export default async
43
- function (req, sw)\` returning a \`Response\`. Params: \`params.id\` there,
44
- \`sw.params.id\` bare; \`somewhere typecheck\` types bare handlers too.
43
+ function (req: Request, sw: SomewhereRuntimeContext)\` returning a \`Response\`.
44
+ Params: \`params.id\` there, \`sw.params.id\` bare.
45
45
 
46
46
  Data: \`id()\` is a random text key; use \`text()\` for references to it.
47
47
  Declare tables in \`db/schema.ts\`; pages use \`somewhere:data\`
@@ -136,8 +136,8 @@ unused \`types/app.ts\` too. A missing optional include directory is harmless.
136
136
  The greeting's \`serverOnly()\` scope demonstrates a trusted public endpoint;
137
137
  it is not the default for your app's data. Choose fitting declared access
138
138
  before the first deploy: \`owner()\` for personal rows, \`group()\` for teams,
139
- and \`anyOf()\` for combined access. Read \`somewhere docs sw.db --section
140
- provider-or-booker-and-owner-or-staff-recipes\` for booking and help-desk rules.
139
+ and \`anyOf()\` for combined access. For booking and help-desk rules, read:
140
+ \`somewhere docs sw.db --section provider-or-booker-and-owner-or-staff-recipes\`
141
141
 
142
142
  \`\`\`sh
143
143
  somewhere typecheck
@@ -1,8 +1,8 @@
1
1
  export const BUNDLED_SKILLS_PACK = {
2
2
  "schema": 1,
3
3
  "name": "somewhere-skills",
4
- "version": "1+a9b0d6a4b518",
5
- "sha256": "a9b0d6a4b5185d8088357c156a7b5b222ebbbd31639113120818998488bcef50",
4
+ "version": "1+e9c2e63a2883",
5
+ "sha256": "e9c2e63a2883be49882eefce95247feed462a3291e0456c752bc930082427da0",
6
6
  "files": [
7
7
  {
8
8
  "path": "deploy-verify-loop/SKILL.md",
@@ -11,8 +11,8 @@ export const BUNDLED_SKILLS_PACK = {
11
11
  },
12
12
  {
13
13
  "path": "schema-changes/SKILL.md",
14
- "sha256": "1b215b59474cec396c53c1fdbc3432b5b3a21f3fa8909580f1d18c640e0f746f",
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\nThe default `somewhere init` starter has no `db/schema.ts`: create it at the project root, or start with `somewhere init --features private-data`, which includes one.\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\nBefore the first deploy, delete an unused starter table declaration outright: there is no deployed table to retire. After a table has been deployed, use `removedTable()` as described above, including when the table is empty.\n\nDeclare a file collection in `db/schema.ts`: who may upload, read, replace and delete its files.\n\nRun `somewhere typecheck` before deploying schema changes.\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\nOn an older shared database, ordinary `sw.db.query` / `sw.db.batch` fail before transport with `MANAGED_RAW_SQL_REQUIRES_SERVER_AUTHORITY`, including in job, queue, and cron handlers; with a separate SQL database they run there and never see declared tables. Fix: write with `sw.db.insert` / `sw.db.update` / `sw.db.remove` (the table needs a declared intent)\n\nSources (`somewhere docs <topic>`): declared-data, declared-files, getting-started, sw.db, troubleshooting.\n"
14
+ "sha256": "c444066ee021b82979a2fa998a9f44a1f463aa9ed5d6429cce9d8f181cd7560f",
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\nThe default `somewhere init` starter has no `db/schema.ts`: create it at the project root, or start with `somewhere init --features private-data`, which includes one.\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\nBefore the first deploy, delete an unused starter table declaration outright: there is no deployed table to retire. After a table has been deployed, mark it `removedTable()` to retire it with 30-day data retention, including when the table is empty.\n\nDeclare a file collection in `db/schema.ts`: who may upload, read, replace and delete its files.\n\nRun `somewhere typecheck` before deploying schema changes.\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\nOn an older shared database, ordinary `sw.db.query` / `sw.db.batch` fail before transport with `MANAGED_RAW_SQL_REQUIRES_SERVER_AUTHORITY`, including in job, queue, and cron handlers; with a separate SQL database they run there and never see declared tables. Fix: write with `sw.db.insert` / `sw.db.update` / `sw.db.remove` (the table needs a declared intent)\n\nSources (`somewhere docs <topic>`): declared-data, declared-files, getting-started, sw.db, troubleshooting.\n"
16
16
  },
17
17
  {
18
18
  "path": "sign-in-setup/SKILL.md",
@@ -26,8 +26,8 @@ export const BUNDLED_SKILLS_PACK = {
26
26
  },
27
27
  {
28
28
  "path": "realtime-and-background/SKILL.md",
29
- "sha256": "49d94fed764c3613e285601089967e89f48489043b037b468323820af8ea2c4d",
30
- "content": "---\nname: realtime-and-background\ndescription: When the page must update without a reload, or work must run outside one request.\n---\n\n# Live views, 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\n## Job: slow work with a status\n\nRun work that shouldn't block a user request in a job. 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(request))) {\n return Response.json({ error: 'forbidden' }, { status: 403 });\n}\n```\n\n## Cron: on a schedule\n\nSchedules run on every plan. 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>`): cron, live, sw.jobs, troubleshooting.\n"
29
+ "sha256": "57cfe286888621dc49023c155a41f584f87647bc5abfa1f813f590685d90fd28",
30
+ "content": "---\nname: realtime-and-background\ndescription: When the page must update without a reload, or work must run outside one request.\n---\n\n# Live views, 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: Request, sw: SomewhereRuntimeContext) {\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\n## Job: slow work with a status\n\nRun work that shouldn't block a user request in a job. 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(request))) {\n return Response.json({ error: 'forbidden' }, { status: 403 });\n}\n```\n\n## Cron: on a schedule\n\nSchedules run on every plan. 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>`): cron, live, sw.jobs, 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,mnEAAmnE;SAC/nE;QACD;YACE,MAAM,EAAE,yBAAyB;YACjC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,g+EAAg+E;SAC5+E;QACD;YACE,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,k2FAAk2F;SAC92F;QACD;YACE,MAAM,EAAE,0BAA0B;YAClC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,s2DAAs2D;SACl3D;QACD;YACE,MAAM,EAAE,kCAAkC;YAC1C,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,uvEAAuvE;SACnwE;QACD;YACE,MAAM,EAAE,+BAA+B;YACvC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,k3DAAk3D;SAC93D;QACD;YACE,MAAM,EAAE,iBAAiB;YACzB,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,65FAA65F;SACz6F;KACF;CACF,CAAC"}
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,mnEAAmnE;SAC/nE;QACD;YACE,MAAM,EAAE,yBAAyB;YACjC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,y/EAAy/E;SACrgF;QACD;YACE,MAAM,EAAE,wBAAwB;YAChC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,k2FAAk2F;SAC92F;QACD;YACE,MAAM,EAAE,0BAA0B;YAClC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,s2DAAs2D;SACl3D;QACD;YACE,MAAM,EAAE,kCAAkC;YAC1C,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,yxEAAyxE;SACryE;QACD;YACE,MAAM,EAAE,+BAA+B;YACvC,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,k3DAAk3D;SAC93D;QACD;YACE,MAAM,EAAE,iBAAiB;YACzB,QAAQ,EAAE,kEAAkE;YAC5E,SAAS,EAAE,65FAA65F;SACz6F;KACF;CACF,CAAC"}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@somewhere-cloud/cli",
3
- "version": "0.38.1",
3
+ "version": "0.38.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@somewhere-cloud/cli",
9
- "version": "0.38.1",
9
+ "version": "0.38.3",
10
10
  "bundleDependencies": [
11
11
  "@modelcontextprotocol/sdk",
12
12
  "chokidar",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@somewhere-cloud/cli",
3
- "version": "0.38.1",
3
+ "version": "0.38.3",
4
4
  "description": "CLI for somewhere.cloud — auth, projects, deploy, pull, promote, db, logs, env, MCP bridge.",
5
5
  "keywords": [
6
6
  "somewhere",