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 +52 -22
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +6 -0
- package/dist/index.js +85 -10
- package/dist/platform-auth.d.ts.map +1 -1
- package/dist/platform-auth.js +2 -0
- package/llms.txt +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,44 +1,74 @@
|
|
|
1
1
|
# oneclient
|
|
2
2
|
|
|
3
|
-
One package for the complete OneClient TypeScript SDK and
|
|
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
|
|
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:
|
|
18
|
-
|
|
23
|
+
baseUrl: import.meta.env.VITE_ONECLIENT_API_URL,
|
|
24
|
+
publishableKey: import.meta.env.VITE_ONECLIENT_PUBLISHABLE_KEY,
|
|
19
25
|
});
|
|
20
26
|
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
oneclient
|
|
29
|
-
|
|
30
|
-
oneclient
|
|
31
|
-
oneclient
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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)
|
package/dist/config.d.ts
ADDED
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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":"
|
|
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"}
|
package/dist/platform-auth.js
CHANGED
|
@@ -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
|