oneclient 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,44 +1,74 @@
1
1
  # oneclient
2
2
 
3
- One package for the complete OneClient TypeScript SDK and its developer CLI.
3
+ One package for the complete OneClient TypeScript SDK and developer CLI.
4
4
 
5
5
  ```bash
6
6
  npm install oneclient
7
7
  npx oneclient login --email you@company.com
8
- npx oneclient init
8
+ npx oneclient init --project-name "My app"
9
9
  ```
10
10
 
11
- The same package works in code:
11
+ The hosted CLI already knows the production control API. Set `ONECLIENT_API_URL` only when a command
12
+ targets a project environment or a self-hosted/local control plane.
13
+
14
+ ## Browser application
15
+
16
+ `oneclient init` shows the development API URL, environment ID, and initial `pk_*`/`sk_*` values
17
+ once. A publishable key is safe to embed in browser code; server and deploy keys are not.
12
18
 
13
19
  ```ts
14
20
  import { createClient } from "oneclient";
15
21
 
16
22
  const one = createClient({
17
- baseUrl: process.env.ONECLIENT_API_URL!,
18
- serverKey: process.env.ONECLIENT_SERVER_KEY!,
23
+ baseUrl: import.meta.env.VITE_ONECLIENT_API_URL,
24
+ publishableKey: import.meta.env.VITE_ONECLIENT_PUBLISHABLE_KEY,
19
25
  });
20
26
 
21
- const notes = await one.from("notes").select("id", "title").limit(20).execute();
27
+ await one.auth.sendOtp({ email: "developer@example.com" });
28
+ ```
29
+
30
+ ## Schema, policy, and local auth origin
31
+
32
+ ```bash
33
+ export ONECLIENT_API_URL=https://dev_REFERENCE.api.one-client.com
34
+ export ONECLIENT_SERVER_KEY=sk_REDACTED
35
+
36
+ npx oneclient sql --file migrations/001_create_feedback.sql
37
+ npx oneclient policy:publish --table feedback --file oneclient.policy.json
38
+ npx oneclient auth-origin:add --environment env_DEVELOPMENT --origin http://localhost:5173
22
39
  ```
23
40
 
24
- Useful commands:
41
+ Each SQL file contains one statement so its read, write, and retained-storage bounds are explicit.
42
+
43
+ ## Development deployment
25
44
 
26
45
  ```bash
27
- oneclient status
28
- oneclient projects
29
- oneclient usage
30
- oneclient deployments --environment env_...
31
- oneclient models
32
- oneclient billing:topup --usd 25
33
- oneclient doctor
34
- oneclient docs
35
- oneclient mcp
46
+ npm run build
47
+ npx oneclient artifact:create \
48
+ --directory . \
49
+ --manifest .oneclient/manifest.json \
50
+ --archive .oneclient/app.tar \
51
+ --framework vite \
52
+ --build-command "npm run build" \
53
+ --output-directory dist \
54
+ --lockfile npm
55
+
56
+ npx oneclient deploy-token:create --environment env_DEVELOPMENT --name local-cli
57
+ export ONECLIENT_DEPLOY_TOKEN=dp_VALUE_SHOWN_ONCE
58
+ npx oneclient deploy \
59
+ --manifest .oneclient/manifest.json \
60
+ --archive .oneclient/app.tar \
61
+ --project prj_PROJECT \
62
+ --environment env_DEVELOPMENT \
63
+ --wait
64
+
65
+ # The deploy token is an environment key; revoke it by the id returned at creation.
66
+ npx oneclient key:revoke --environment env_DEVELOPMENT --key key_DEPLOY_TOKEN
36
67
  ```
37
68
 
38
- Interactive terminals receive a compact, colored interface. Pipes and CI receive stable JSON by
39
- default; `--json` makes that behavior explicit. Developer login credentials are stored in an
40
- owner-only local file. `sk_*` and `dp_*` credentials stay in environment variables and are never
41
- persisted by the CLI.
69
+ Interactive terminals receive a compact interface. Pipes and CI receive stable JSON automatically;
70
+ `--json` makes that behavior explicit. Developer sessions are stored in an owner-only local file.
71
+ `sk_*` and `dp_*` values stay in environment variables and are never persisted by the CLI.
42
72
 
43
- Documentation: [one-client.com/docs](https://one-client.com/docs) · AI context:
44
- [one-client.com/llms.txt](https://one-client.com/llms.txt)
73
+ Full quickstart: [one-client.com/docs/quickstart](https://one-client.com/docs/quickstart) · Agent
74
+ context: [one-client.com/llms.txt](https://one-client.com/llms.txt)
@@ -0,0 +1,4 @@
1
+ export declare const DEFAULT_CONTROL_API_URL = "https://control.one-client.com";
2
+ export declare const DEFAULT_CONSOLE_ORIGIN = "https://console.one-client.com";
3
+ export declare function developerAuthOrigin(apiUrl: string): string;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AACxE,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AAEvE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG1D"}
package/dist/config.js ADDED
@@ -0,0 +1,6 @@
1
+ export const DEFAULT_CONTROL_API_URL = "https://control.one-client.com";
2
+ export const DEFAULT_CONSOLE_ORIGIN = "https://console.one-client.com";
3
+ export function developerAuthOrigin(apiUrl) {
4
+ const origin = new URL(apiUrl).origin;
5
+ return origin === DEFAULT_CONTROL_API_URL ? DEFAULT_CONSOLE_ORIGIN : origin;
6
+ }
package/dist/index.js CHANGED
@@ -14,11 +14,12 @@ import { deployTokenFromEnvironment, parseServerKeyScopes, serverKeyFromEnvironm
14
14
  import { secretValueFromOptions } from "./secret-input.js";
15
15
  import { brand, formatOutput, statusLine } from "./presenter.js";
16
16
  import { slugify } from "./slug.js";
17
+ import { DEFAULT_CONTROL_API_URL } from "./config.js";
17
18
  const program = new Command();
18
19
  program
19
20
  .name("oneclient")
20
21
  .description("Deploy and manage OneClient projects")
21
- .version("0.1.0")
22
+ .version("0.1.1")
22
23
  .option("--api-url <url>", "Control or environment API URL", process.env.ONECLIENT_API_URL)
23
24
  .option("--organization <id>", "Organization ID", process.env.ONECLIENT_ORGANIZATION_ID)
24
25
  .option("--json", "Print stable JSON for scripts and agents", false)
@@ -29,7 +30,7 @@ program
29
30
  .description("Sign in as a platform developer with an email OTP")
30
31
  .requiredOption("--email <email>")
31
32
  .action(async (options) => {
32
- const apiUrl = await resolveApiUrl();
33
+ const apiUrl = await resolveControlApiUrl();
33
34
  await runStep(`Sending a private sign-in code to ${options.email.trim()}`, () => sendDeveloperOtp(apiUrl, options.email));
34
35
  const otp = process.env.ONECLIENT_OTP ?? (await promptForOtp());
35
36
  const sessionToken = await runStep("Verifying your code", () => verifyDeveloperOtp(apiUrl, options.email, otp));
@@ -213,7 +214,9 @@ program
213
214
  .command("doctor")
214
215
  .description("Check local credentials and live OneClient connectivity")
215
216
  .action(async () => {
217
+ const options = program.opts();
216
218
  const credentials = await resolvedDeveloperCredentials();
219
+ const controlApi = options.apiUrl ?? credentials?.apiUrl ?? DEFAULT_CONTROL_API_URL;
217
220
  let account = null;
218
221
  let controlReachable = false;
219
222
  if (credentials) {
@@ -221,9 +224,20 @@ program
221
224
  account = response.user.email;
222
225
  controlReachable = true;
223
226
  }
227
+ else {
228
+ try {
229
+ const response = await fetch(new URL("/v1/me", `${controlApi}/`), {
230
+ headers: { Accept: "application/json" },
231
+ });
232
+ controlReachable = response.status < 500;
233
+ }
234
+ catch {
235
+ controlReachable = false;
236
+ }
237
+ }
224
238
  print({
225
239
  node: process.version,
226
- controlApi: credentials?.apiUrl ?? null,
240
+ controlApi,
227
241
  signedIn: Boolean(credentials),
228
242
  account,
229
243
  controlReachable,
@@ -372,6 +386,53 @@ program
372
386
  .limit(Number(options.limit))
373
387
  .execute()));
374
388
  });
389
+ program
390
+ .command("sql")
391
+ .description("Execute one bounded SQL statement or trusted server query from a file")
392
+ .requiredOption("--file <path>")
393
+ .option("--max-rows-read <rows>", "Maximum rows the statement may read", "1000")
394
+ .option("--max-rows-written <rows>", "Maximum rows the statement may write", "100")
395
+ .option("--max-storage-delta-bytes <bytes>", "Maximum retained storage increase", "65536")
396
+ .action(async (options) => {
397
+ const maxRowsRead = positiveInteger(options.maxRowsRead, "max-rows-read");
398
+ const maxRowsWritten = positiveInteger(options.maxRowsWritten, "max-rows-written");
399
+ const maxStorageDeltaBytes = nonNegativeInteger(options.maxStorageDeltaBytes, "max-storage-delta-bytes");
400
+ const sql = (await readFile(options.file, "utf8")).trim();
401
+ if (!sql)
402
+ throw new Error("SQL file cannot be empty");
403
+ print(await serverClient().then((api) => api.sql.execute({
404
+ sql,
405
+ maxRowsRead,
406
+ maxRowsWritten,
407
+ maxStorageDeltaBytes,
408
+ })), "SQL completed");
409
+ });
410
+ program
411
+ .command("policy:publish")
412
+ .description("Validate and activate a deny-by-default data policy from a JSON file")
413
+ .requiredOption("--table <table>")
414
+ .requiredOption("--file <path>")
415
+ .action(async (options) => {
416
+ const policy = JSON.parse(await readFile(options.file, "utf8"));
417
+ print(await serverClient().then((api) => api.data.publishPolicy(options.table, policy)), "Policy activated");
418
+ });
419
+ program
420
+ .command("auth-origin:list")
421
+ .description("List trusted browser and native authentication origins")
422
+ .requiredOption("--environment <id>")
423
+ .action(async (options) => print(await sessionClient().then((api) => api.authOrigins.list(options.environment))));
424
+ program
425
+ .command("auth-origin:add")
426
+ .description("Trust a browser origin or native application scheme for authentication")
427
+ .requiredOption("--environment <id>")
428
+ .requiredOption("--origin <origin>")
429
+ .action(async (options) => print(await sessionClient().then((api) => api.authOrigins.create(options.environment, options.origin)), "Authentication origin added"));
430
+ program
431
+ .command("auth-origin:delete")
432
+ .description("Remove a trusted authentication origin")
433
+ .requiredOption("--environment <id>")
434
+ .requiredOption("--origin-id <id>")
435
+ .action(async (options) => print(await sessionClient().then((api) => api.authOrigins.delete(options.environment, options.originId))));
375
436
  program
376
437
  .command("artifact:create")
377
438
  .description("Create a validated manifest and tar archive from a project directory")
@@ -517,7 +578,7 @@ async function deployClient() {
517
578
  if (!deployToken?.startsWith("dp_"))
518
579
  throw new Error("This command requires ONECLIENT_DEPLOY_TOKEN with a dp_* token");
519
580
  return createClient({
520
- baseUrl: await resolveApiUrl(),
581
+ baseUrl: await resolveControlApiUrl(),
521
582
  deployToken,
522
583
  ...(options.organization ? { organizationId: options.organization } : {}),
523
584
  });
@@ -528,7 +589,7 @@ async function serverClient() {
528
589
  if (!serverKey?.startsWith("sk_"))
529
590
  throw new Error("This command requires ONECLIENT_SERVER_KEY with an sk_* key");
530
591
  return createClient({
531
- baseUrl: await resolveApiUrl(),
592
+ baseUrl: resolveEnvironmentApiUrl(),
532
593
  serverKey,
533
594
  ...(options.organization ? { organizationId: options.organization } : {}),
534
595
  });
@@ -539,18 +600,32 @@ async function resolvedDeveloperCredentials() {
539
600
  const sessionToken = process.env.ONECLIENT_SESSION_TOKEN ?? stored?.sessionToken;
540
601
  if (!sessionToken)
541
602
  return null;
542
- const apiUrl = options.apiUrl ?? stored?.apiUrl;
543
- if (!apiUrl)
544
- throw new Error("Set ONECLIENT_API_URL or pass --api-url");
603
+ const apiUrl = options.apiUrl ?? stored?.apiUrl ?? DEFAULT_CONTROL_API_URL;
545
604
  return { apiUrl, sessionToken };
546
605
  }
547
- async function resolveApiUrl() {
606
+ async function resolveControlApiUrl() {
548
607
  const options = program.opts();
549
608
  const apiUrl = options.apiUrl ?? (await readDeveloperCredentials())?.apiUrl;
609
+ return apiUrl ?? DEFAULT_CONTROL_API_URL;
610
+ }
611
+ function resolveEnvironmentApiUrl() {
612
+ const apiUrl = program.opts().apiUrl;
550
613
  if (!apiUrl)
551
- throw new Error("Set ONECLIENT_API_URL or pass --api-url");
614
+ throw new Error("Set ONECLIENT_API_URL to the environment API URL or pass --api-url https://<environment>.api.one-client.com");
552
615
  return apiUrl;
553
616
  }
617
+ function positiveInteger(value, name) {
618
+ const parsed = Number(value);
619
+ if (!Number.isSafeInteger(parsed) || parsed < 1)
620
+ throw new Error(`${name} must be a positive integer`);
621
+ return parsed;
622
+ }
623
+ function nonNegativeInteger(value, name) {
624
+ const parsed = Number(value);
625
+ if (!Number.isSafeInteger(parsed) || parsed < 0)
626
+ throw new Error(`${name} must be a non-negative integer`);
627
+ return parsed;
628
+ }
554
629
  async function promptForOtp() {
555
630
  if (!process.stdin.isTTY)
556
631
  throw new Error("Set ONECLIENT_OTP for a non-interactive login");
@@ -1 +1 @@
1
- {"version":3,"file":"platform-auth.d.ts","sourceRoot":"","sources":["../src/platform-auth.ts"],"names":[],"mappings":"AAOA,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,IAAI,CAAC,CAEf"}
1
+ {"version":3,"file":"platform-auth.d.ts","sourceRoot":"","sources":["../src/platform-auth.ts"],"names":[],"mappings":"AAQA,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,OAAO,UAAU,CAAC,KAAwB,GAClD,OAAO,CAAC,IAAI,CAAC,CAEf"}
@@ -1,4 +1,5 @@
1
1
  import { validateApiUrl } from "./credentials.js";
2
+ import { developerAuthOrigin } from "./config.js";
2
3
  export async function sendDeveloperOtp(apiUrl, email, fetcher = globalThis.fetch) {
3
4
  await authRequest(fetcher, apiUrl, "/v1/platform/auth/email-otp/send-verification-otp", {
4
5
  email: normalizeEmail(email),
@@ -24,6 +25,7 @@ async function authRequest(fetcher, apiUrl, path, body, sessionToken) {
24
25
  headers: {
25
26
  Accept: "application/json",
26
27
  "Content-Type": "application/json",
28
+ Origin: developerAuthOrigin(apiUrl),
27
29
  ...(sessionToken ? { Authorization: `Bearer ${sessionToken}` } : {}),
28
30
  },
29
31
  body: JSON.stringify(body),
package/llms.txt CHANGED
@@ -11,6 +11,14 @@ MCP: https://control.one-client.com/mcp
11
11
 
12
12
  Install: npm install oneclient
13
13
  CLI login: npx oneclient login --email you@company.com
14
+ Control API configuration is automatic for the hosted service.
15
+
16
+ First project flow:
17
+ 1. npx oneclient init --project-name "My app"
18
+ 2. Capture the shown-once development pk_* and sk_* values.
19
+ 3. Apply SQL with `oneclient sql` and policy with `oneclient policy:publish`.
20
+ 4. Add local/custom auth origins with `oneclient auth-origin:add`.
21
+ 5. Build, run `oneclient artifact:create`, create a dp_* token, then run `oneclient deploy --wait`.
14
22
 
15
23
  Use pk_* only in browser/mobile clients, sk_* only on trusted servers, and dp_* only for deploys.
16
24
  Never expose sk_* or dp_* values in client bundles or model output. Every mutation is idempotent and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oneclient",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "One package for the complete OneClient TypeScript SDK and developer CLI.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://one-client.com",