Skip to content

Commit bab64b9

Browse files
Merge branch 'main' into user-agent
2 parents f740284 + b211a9e commit bab64b9

40 files changed

Lines changed: 437 additions & 89 deletions

.github/workflows/onRelease.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
cache: 'pnpm'
1717
- run: pnpm install --frozen-lockfile
1818
- run: pnpm run build
19-
- name: Publish @powersync/cli to npm
20-
run: pnpm publish -- --no-git-checks --access public
21-
env:
22-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
19+
# - name: Publish @powersync/cli to npm
20+
# run: pnpm publish -- --no-git-checks --access public
21+
# env:
22+
# NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

AGENTS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,12 @@
1111
- Prefer mocking env helpers/modules in tests instead of relying on direct `process.env` reads in production code.
1212
- Tests may set temporary env values when needed, but should primarily validate behavior through mocked env access points.
1313
- Reset env-related mocks/state between tests to avoid leakage.
14+
15+
## File Naming Conventions
16+
17+
- Choose the filename based on the file's primary responsibility so agents can infer intent without opening the file.
18+
- If the main export is a class or a type/interface, the filename must exactly match that export name (for example, `ServiceCloudConfig.ts`, `AccountsHubClientSDKClient.ts`).
19+
- Use this class/type naming rule even if the file also contains small helper functions; the primary exported symbol takes precedence.
20+
- If the file's purpose is utility logic (single function or a group of helper methods), use action-style kebab-case names in the form `do-this-action.ts` (for example, `ensure-service-type.ts`, `resolve-config-path.ts`).
21+
- Utility filenames should describe what the code does, not what it is. Prefer verb-led names such as `load-*`, `parse-*`, `validate-*`, `write-*`, `ensure-*`.
22+
- Avoid generic utility names like `helpers.ts`, `utils.ts`, or `common.ts` unless the file is intentionally a broad shared entry point.

cli/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ npx powersync --version
4242
The PowerSync CLI lets you manage PowerSync instances and run commands (generate schemas, tokens, validate config, fetch status, and more). Support is split into two modes:
4343

4444
- **Cloud** – Full support for [PowerSync Cloud](https://powersync.com). You can create new instances, deploy and pull config from the Dashboard, and run all Cloud commands. Authenticate with **`powersync login`** (or the `PS_ADMIN_TOKEN` env var), then use **`powersync init cloud`** / **`powersync link cloud`** or **`powersync pull instance`** to work with projects.
45-
- **Self-hosted** – Limited support for your own PowerSync Service. You link to an existing running instance and can run a subset of commands (e.g. **`powersync fetch status`**, **`powersync generate schema`**, **`powersync validate`**). The CLI does not create, deploy to, or pull config from self-hosted instances; you manage the server and its config yourself. We also expose a [PowerSync Docker topic](../plugins/docker/README.md) for local self-hosted development.
45+
- **Self-hosted** – Limited support for your own PowerSync Service. You link to an existing running instance and can run a subset of commands (e.g. **`powersync status`**, **`powersync generate schema`**, **`powersync validate`**). The CLI does not create, deploy to, or pull config from self-hosted instances; you manage the server and its config yourself. We also expose a [PowerSync Docker topic](../plugins/docker/README.md) for local self-hosted development.
4646

4747
The sections below go into detail for [Cloud](#cloud) and [Self-hosted](#self-hosted).
4848

@@ -126,7 +126,7 @@ To refresh local config after external edits from the cloud when already linked,
126126

127127
## Running commands against externally managed instances
128128

129-
You can run CLI commands (e.g. **`powersync generate schema`**, **`powersync generate token`**, **`powersync fetch status`**) against a Cloud instance whose configuration is managed elsewhere—for example in the PowerSync Dashboard. No local config directory or link file is required.
129+
You can run CLI commands (e.g. **`powersync generate schema`**, **`powersync generate token`**, **`powersync status`**) against a Cloud instance whose configuration is managed elsewhere—for example in the PowerSync Dashboard. No local config directory or link file is required.
130130

131131
Specify the instance using **environment variables** or **CLI flags** (flags take precedence): `--instance-id` and `--project-id` (or `INSTANCE_ID`, `PROJECT_ID`). **`--org-id` is optional**: when omitted, the CLI uses the token’s single organization if the token has access to exactly one; if the token has multiple orgs, you must pass **`--org-id`** (or set `ORG_ID`).
132132

@@ -143,7 +143,7 @@ powersync generate schema --output-path=schema.ts --output=ts
143143

144144
# Self-hosted
145145

146-
The CLI can run a subset of commands against **self-hosted** PowerSync instances (your own API). Self-hosted support is more limited than Cloud: you link to an existing running API and use the same config directory concept, but only certain commands apply (e.g. **`powersync fetch status`**, **`powersync generate schema`**, **`powersync generate token`**, **`powersync validate`**). There is no **deploy** or **pull instance** for self-hosted; you manage config on the server yourself.
146+
The CLI can run a subset of commands against **self-hosted** PowerSync instances (your own API). Self-hosted support is more limited than Cloud: you link to an existing running API and use the same config directory concept, but only certain commands apply (e.g. **`powersync status`**, **`powersync generate schema`**, **`powersync generate token`**, **`powersync validate`**). There is no **deploy** or **pull instance** for self-hosted; you manage config on the server yourself.
147147

148148
## Authentication
149149

@@ -169,13 +169,13 @@ The CLI resolves **`!env PS_ADMIN_TOKEN`** from the `PS_ADMIN_TOKEN` environment
169169

170170
## Creating a self-hosted project and limitations
171171

172-
Run **`powersync init self-hosted`** to scaffold a config directory. Edit **`service.yaml`** with your instance details and use **`!env`** for secrets. This gives you a **partial** project: the CLI does not create or provision a self-hosted instance. You must already have a running PowerSync API. The CLI cannot deploy config to or pull config from a self-hosted instance; you manage **`service.yaml`** and **`sync-config.yaml`** on the server yourself. Use the CLI to link (**`powersync link self-hosted --api-url <url>`**), then run the supported commands (e.g. **`powersync fetch status`**, **`powersync generate schema`**) against that API.
172+
Run **`powersync init self-hosted`** to scaffold a config directory. Edit **`service.yaml`** with your instance details and use **`!env`** for secrets. This gives you a **partial** project: the CLI does not create or provision a self-hosted instance. You must already have a running PowerSync API. The CLI cannot deploy config to or pull config from a self-hosted instance; you manage **`service.yaml`** and **`sync-config.yaml`** on the server yourself. Use the CLI to link (**`powersync link self-hosted --api-url <url>`**), then run the supported commands (e.g. **`powersync status`**, **`powersync generate schema`**) against that API.
173173

174174
```sh
175175
powersync init self-hosted
176176
# then edit powersync/service.yaml
177177
powersync link self-hosted --api-url https://powersync.example.com
178-
powersync fetch status
178+
powersync status
179179
```
180180

181181
Use `--directory` for a different config folder.
@@ -186,7 +186,7 @@ We expose a [PowerSync Docker topic](../plugins/docker/README.md) for running a
186186

187187
## Command support
188188

189-
Only some CLI commands work with self-hosted instances. Supported commands include **`powersync fetch status`**, **`powersync generate schema`**, **`powersync generate token`**, **`powersync validate`**, and **`powersync link self-hosted`**. Cloud-only commands such as **`powersync deploy`**, **`powersync destroy`**, **`powersync pull instance`**, **`powersync fetch config`**, and **`powersync fetch instances`** do not apply to self-hosted.
189+
Only some CLI commands work with self-hosted instances. Supported commands include **`powersync status`**, **`powersync generate schema`**, **`powersync generate token`**, **`powersync validate`**, and **`powersync link self-hosted`**. Cloud-only commands such as **`powersync deploy`**, **`powersync destroy`**, **`powersync pull instance`**, **`powersync fetch config`**, and **`powersync fetch instances`** do not apply to self-hosted.
190190

191191
# Known Limitations
192192

@@ -245,7 +245,7 @@ You can supply instance and auth context via environment variables (useful for C
245245
Example (Cloud):
246246

247247
```sh
248-
PS_ADMIN_TOKEN=your-token PROJECT_ID=456 INSTANCE_ID=789 powersync fetch status
248+
PS_ADMIN_TOKEN=your-token PROJECT_ID=456 INSTANCE_ID=789 powersync status
249249
```
250250

251251
See [docs/usage.md](../docs/usage.md) for full usage and resolution order (flags, env, cli.yaml).
@@ -560,7 +560,7 @@ DESCRIPTION
560560

561561
Run `docker compose down` then `docker compose up -d --wait`: stops and removes containers, then starts the stack and
562562
waits for services (including PowerSync) to be healthy. Use when you want a clean bring-up (e.g. after config
563-
changes). Use `powersync fetch status` to debug running instances.
563+
changes). Use `powersync status` to debug running instances.
564564

565565
EXAMPLES
566566
$ powersync docker reset
@@ -586,7 +586,7 @@ DESCRIPTION
586586
Start the self-hosted PowerSync stack via Docker Compose.
587587

588588
Runs `docker compose up -d --wait` for the project docker/ compose file; waits for services (including PowerSync) to
589-
be healthy. Use `powersync fetch status` to debug running instances.
589+
be healthy. Use `powersync status` to debug running instances.
590590

591591
EXAMPLES
592592
$ powersync docker start

cli/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@
109109
"build": "tsc -b && pnpm prepack",
110110
"lint": "eslint",
111111
"postpack": "shx rm -f oclif.manifest.json",
112-
"posttest": "pnpm run lint",
113112
"pretest": "pnpm run build",
114113
"readme:generate": "oclif readme && pnpm exec prettier --write README.md",
115114
"prepack": "oclif manifest && pnpm run readme:generate",

cli/src/commands/deploy/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ export default class DeployAll extends CloudInstanceCommand {
317317
sync_rules: project.syncRulesContent ?? ''
318318
})
319319
.catch((error) => {
320-
if (retry === 9) {
320+
if (retry === 99) {
321321
this.styledError({
322322
error,
323323
message: `Failed to validate sync config for instance ${project.linked.instance_id} in project ${project.linked.project_id} in org ${project.linked.org_id}. Ensure the sync config is valid before deploying.`,
@@ -342,6 +342,9 @@ export default class DeployAll extends CloudInstanceCommand {
342342
suggestions: ['Check your sync config and try again.']
343343
});
344344
}
345+
346+
// Validation succeeded with no errors
347+
return;
345348
}
346349
}
347350

@@ -381,7 +384,7 @@ export default class DeployAll extends CloudInstanceCommand {
381384
this.log(ux.colorize('green', 'Deployment operation completed successfully!'));
382385
} else {
383386
this.styledError({
384-
message: `Deploy failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync fetch status')}`
387+
message: `Deploy failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync status')}`
385388
});
386389
}
387390
}

cli/src/commands/deploy/sync-config.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { ux } from '@oclif/core/ux';
22
import { routes } from '@powersync/management-types';
33

4+
import { DEFAULT_DEPLOY_TIMEOUT_MS } from '../../api/cloud/wait-for-operation.js';
45
import DeployAll from './index.js';
56

6-
export class DeploySyncConfig extends DeployAll {
7+
export default class DeploySyncConfig extends DeployAll {
78
static description = 'Deploy only sync config changes.';
89
static examples = [
910
'<%= config.bin %> <%= command.id %>',
@@ -55,7 +56,7 @@ export class DeploySyncConfig extends DeployAll {
5556
}
5657

5758
async run(): Promise<void> {
58-
const { flags } = await this.parse(DeployAll);
59+
const { flags } = await this.parse(DeploySyncConfig);
5960

6061
const project = await this.loadProject(flags, {
6162
// We don't need the config to be managed locally for this
@@ -65,6 +66,8 @@ export class DeploySyncConfig extends DeployAll {
6566
const { linked } = project;
6667
this.parseConfig(project.projectDirectory);
6768

69+
const deployTimeoutMs = (flags['deploy-timeout'] ?? DEFAULT_DEPLOY_TIMEOUT_MS / 1000) * 1000;
70+
6871
// The existing config is required to deploy changes. The instance should have been created already.
6972
const cloudConfigState = await this.loadCloudConfigState();
7073

@@ -97,7 +100,7 @@ export class DeploySyncConfig extends DeployAll {
97100
`\nThe instance is not currently provisioned. Triggering a deploy in order to reprovision. This may take a few minutes.\n`
98101
);
99102
// Don't yet update the sync config since the instance is not provisioned, but deploy to trigger provisioning
100-
await this.deployAll({ cloudConfigState, deployTimeoutMs: flags.timeout, updateSyncConfig: false });
103+
await this.deployAll({ cloudConfigState, deployTimeoutMs, updateSyncConfig: false });
101104
}
102105

103106
// Validate sync config
@@ -106,6 +109,6 @@ export class DeploySyncConfig extends DeployAll {
106109

107110
this.log('Validations completed successfully.\n');
108111

109-
await this.deploySyncConfig({ cloudConfigState, timeout: flags.timeout });
112+
await this.deploySyncConfig({ cloudConfigState, timeout: deployTimeoutMs });
110113
}
111114
}

cli/src/commands/destroy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default class Destroy extends CloudInstanceCommand {
3636
spinner.start();
3737

3838
try {
39-
const deactivateResult = await client.deactivateInstance({
39+
const deactivateResult = await client.destroyInstance({
4040
app_id: linked.project_id,
4141
id: linked.instance_id,
4242
org_id: linked.org_id
@@ -56,7 +56,7 @@ export default class Destroy extends CloudInstanceCommand {
5656
this.log(ux.colorize('green', 'Instance destroyed successfully.'));
5757
} else {
5858
this.styledError({
59-
message: `Operation failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync fetch status')}`
59+
message: `Operation failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync status')}`
6060
});
6161
}
6262
} catch (error) {

cli/src/commands/link/cloud.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,14 @@ export default class LinkCloud extends CloudInstanceCommand {
8989
this.styledError({ error, message: 'Failed to create Cloud instance' });
9090
}
9191

92-
const projectDir = this.ensureProjectDirectory({ directory });
9392
ensureServiceTypeMatches({
9493
command: this,
9594
configRequired: false,
9695
directoryLabel: directory,
9796
expectedType: ServiceType.CLOUD,
98-
projectDir
97+
projectDir: projectDirectory
9998
});
100-
writeCloudLink(projectDir, { instanceId: newInstanceId, orgId, projectId });
99+
writeCloudLink(projectDirectory, { instanceId: newInstanceId, orgId, projectId });
101100
this.log(
102101
ux.colorize('green', `Created Cloud instance ${newInstanceId} and updated ${directory}/${CLI_FILENAME}.`)
103102
);

cli/src/commands/logout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export default class Logout extends PowerSyncCommand {
88
static summary = 'Remove stored auth token.';
99

1010
async run(): Promise<void> {
11+
await this.parse(Logout);
1112
const { authentication } = Services;
1213

1314
const token = await authentication.getToken();

cli/src/commands/stop.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,13 @@ export default class Stop extends CloudInstanceCommand {
5252
timeoutMs: 10 * 60 * 1000 // Stopping may take longer than deploying, so use a longer timeout
5353
});
5454

55-
// const status = await waitForOperationStatusChange(client, linked, linked.instance_id, stopResult.operation_id, timeoutMs);
5655
spinner.stop();
5756

5857
if (status === 'completed') {
5958
this.log(ux.colorize('green', 'Instance stopped successfully.'));
6059
} else {
6160
this.styledError({
62-
message: `Operation failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync fetch status')}`
61+
message: `Operation failed. Check instance diagnostics for details, for example: ${ux.colorize('blue', 'powersync status')}`
6362
});
6463
}
6564
} catch (error) {

0 commit comments

Comments
 (0)