Skip to content

Commit 20eed7c

Browse files
ampagentRavindra Barthwal
andcommitted
Release v0.1.0-rc.1
Co-authored-by: Ravindra Barthwal <p8ndotai@gmail.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0d921-e915-746a-b8b0-530e5568dace
1 parent f6db293 commit 20eed7c

19 files changed

Lines changed: 592 additions & 97 deletions

‎apps/docs/astro.config.mjs‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { defineConfig } from "astro/config";
21
import starlight from "@astrojs/starlight";
2+
import { defineConfig } from "astro/config";
33

44
export default defineConfig({
5+
base: "/docs",
56
integrations: [
67
starlight({
78
title: "dx docs",
@@ -39,6 +40,18 @@ export default defineConfig({
3940
label: "Models and providers",
4041
slug: "deployment/models-providers",
4142
},
43+
{
44+
label: "GitHub source access",
45+
slug: "deployment/github-source",
46+
},
47+
{
48+
label: "Bitbucket source access",
49+
slug: "deployment/bitbucket-source",
50+
},
51+
{
52+
label: "GitHub Copilot",
53+
slug: "deployment/github-copilot",
54+
},
4255
{
4356
label: "Upgrade and recovery",
4457
slug: "operations/upgrade-recovery",

‎apps/docs/scripts/check-content.mjs‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { fileURLToPath } from "node:url";
55

66
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
77
const built = process.argv.includes("--built");
8-
const scanRoot = resolve(root, built ? "dist" : "src/content/docs");
8+
const rootArgument = process.argv.indexOf("--root");
9+
const scanRoot =
10+
rootArgument === -1
11+
? resolve(root, built ? "dist" : "src/content/docs")
12+
: resolve(process.argv[rootArgument + 1] ?? "");
913
const scannedExtensions = new Set(
1014
built ? [".css", ".html", ".js", ".json", ".svg"] : [".md", ".mdx"],
1115
);
@@ -34,6 +38,14 @@ const prohibited = [
3438
pattern: new RegExp(`(?<!&)\\b${forbiddenAssistantName}\\b(?!;)`, "gi"),
3539
},
3640
{ label: "private commit SHA", pattern: /\b[0-9a-f]{40}\b/gi },
41+
{
42+
label: "unfinished content marker",
43+
pattern: /\b(?:TODO|TBD|Track [A-D]|not implemented)\b/gi,
44+
},
45+
{
46+
label: "obsolete release language",
47+
pattern: /\b(?:pre-release|prerelease|rehearsal|clean-room)\b/gi,
48+
},
3749
];
3850

3951
const walk = async (directory) => {
@@ -65,7 +77,10 @@ const builtLinkTarget = (sourcePath, href) => {
6577
const sourceRoute = `/${relative(scanRoot, sourcePath).replace(/index[.]html$/, "")}`;
6678
const url = new URL(href, `https://docs.invalid${sourceRoute}`);
6779
if (url.origin !== "https://docs.invalid") return undefined;
68-
const pathname = decodeURIComponent(url.pathname);
80+
const pathname = decodeURIComponent(url.pathname).replace(
81+
/^\/docs(?=\/|$)/,
82+
"",
83+
);
6984
const direct = resolve(scanRoot, pathname.replace(/^\//, ""));
7085
const candidates = pathname.endsWith("/")
7186
? [resolve(direct, "index.html")]
@@ -119,7 +134,10 @@ for (const path of files) {
119134
);
120135
continue;
121136
}
122-
const slug = href.replace(/^\//, "").replace(/\/$/, "");
137+
const slug = href
138+
.replace(/^\/docs(?=\/|$)/, "")
139+
.replace(/^\//, "")
140+
.replace(/\/$/, "");
123141
const candidates = slug
124142
? [
125143
resolve(scanRoot, `${slug}.md`),

‎apps/docs/scripts/check-content.test.ts‎

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { spawnSync } from "node:child_process";
2-
import { existsSync, rmSync } from "node:fs";
2+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3+
import { tmpdir } from "node:os";
4+
import { resolve } from "node:path";
35
import { fileURLToPath } from "node:url";
46
import { describe, expect, it } from "vitest";
57

@@ -17,6 +19,30 @@ describe("docs content checks", () => {
1719
expect(result.stdout).toContain("docs source and internal links passed");
1820
});
1921

22+
it.each([
23+
"TODO: finish this",
24+
"TBD",
25+
"Pending Track C integration",
26+
"This is not implemented.",
27+
"pre-release instructions",
28+
"external clean-room check",
29+
])("rejects unfinished release content: %s", (contents) => {
30+
const fixture = mkdtempSync(resolve(tmpdir(), "dx-docs-content-"));
31+
writeFileSync(resolve(fixture, "page.md"), contents);
32+
const script = fileURLToPath(
33+
new URL("./check-content.mjs", import.meta.url),
34+
);
35+
const result = spawnSync(process.execPath, [script, "--root", fixture], {
36+
encoding: "utf8",
37+
});
38+
rmSync(fixture, { recursive: true, force: true });
39+
40+
expect(result.status).toBe(1);
41+
expect(result.stderr).toMatch(
42+
/unfinished content marker|obsolete release language/,
43+
);
44+
});
45+
2046
it("typechecks through the workspace gate without generated Astro state", () => {
2147
const generatedDirectory = fileURLToPath(
2248
new URL("../.astro", import.meta.url),

‎apps/docs/src/content/docs/contributing/guide.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,5 @@ contributing.
3434

3535
## Documentation style
3636

37-
Write instructions a reader can execute. Mark unreleased behavior instead of
38-
presenting it as current. Use placeholders only when an owning schema or command
39-
has not landed, and name the integration that must replace the placeholder.
37+
Write instructions a reader can execute. Describe behavior that exists in the
38+
current release. Link to the owning schema or command when exact input matters.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
title: Bitbucket source access
3+
description: Register a Bitbucket Cloud OAuth consumer for private repositories and pull requests.
4+
---
5+
6+
Bitbucket integration is optional. Public Git URLs work without it. Configure a
7+
Bitbucket Cloud OAuth consumer when users need private repository discovery,
8+
checkout, pushes, or pull request operations.
9+
10+
## Register the OAuth consumer
11+
12+
You need administrative access to a Bitbucket Cloud workspace. Follow
13+
Atlassian's [OAuth consumer
14+
guide](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/).
15+
16+
1. Open the workspace's **Settings → OAuth consumers** and choose **Add
17+
consumer**.
18+
2. Enter a name and the deployment origin as the website.
19+
3. Set the callback URL to
20+
`<deployment-origin>/v1/integrations/bitbucket/oauth/callback`.
21+
4. Select **Account: Read**, **Repositories: Write**, and **Pull requests:
22+
Write**. Leave unrelated permissions off.
23+
5. Save the consumer and copy its generated key and secret.
24+
25+
For dxcode.dev, the callback is:
26+
27+
```text
28+
https://dxcode.dev/v1/integrations/bitbucket/oauth/callback
29+
```
30+
31+
The `account` scope lets dx identify the user and inspect accessible workspaces.
32+
`repository:write` includes repository read and HTTPS push access.
33+
`pullrequest:write` includes pull request read, create, update, approve, decline,
34+
and merge access. Atlassian defines these scopes in the [Bitbucket OAuth 2.0
35+
reference](https://developer.atlassian.com/cloud/bitbucket/oauth-2#bitbucket-oauth-2-0-scopes).
36+
37+
## Create the installer JSON
38+
39+
The consumer key is the `clientId`. The installer accepts this exact object:
40+
41+
```json
42+
{
43+
"version": 1,
44+
"clientId": "replace-with-consumer-key",
45+
"clientSecret": "replace-with-consumer-secret",
46+
"callbackUrl": "https://dx.example.com/v1/integrations/bitbucket/oauth/callback"
47+
}
48+
```
49+
50+
The callback must use HTTPS and exactly match the deployment origin and path.
51+
Extra fields are rejected. Do not commit this JSON or expose the consumer
52+
secret.
53+
54+
Run `pnpm dx:deploy`, answer **Yes** to **Configure Bitbucket OAuth**, and paste
55+
the object at the hidden **Bitbucket OAuth JSON** prompt. Noninteractive runs set
56+
`integrations` to include `bitbucket` and supply the object through
57+
`DX_INTEGRATION_BITBUCKET_OAUTH`.
58+
59+
## Connect and verify
60+
61+
1. Open **Settings → Integrations → Bitbucket** in dx.
62+
2. Choose **Connect** and authorize the consumer as the Bitbucket user whose
63+
repositories should be available.
64+
3. Create a project from a private Bitbucket repository.
65+
4. Verify checkout from the default branch and create or update a pull request
66+
from an agent branch.
67+
68+
dx refreshes expiring access tokens. **Refresh** rechecks the account and
69+
repository list. Disconnecting in dx deletes its stored authorization and stops
70+
local access. It does not revoke the grant at Bitbucket; use Bitbucket's app
71+
authorization settings for provider-side revocation.
72+
73+
## Rotate or remove access
74+
75+
Create a replacement consumer, then run `pnpm dx:deploy -- --rotate`, choose
76+
Bitbucket, and provide its JSON. Users must reconnect. Verify access before
77+
deleting the old consumer. To remove Bitbucket from a deployment, remove
78+
`bitbucket` from `integrations`, rerun, disconnect stored connections in dx, and
79+
delete or revoke the consumer in Bitbucket.
80+
81+
## Troubleshooting
82+
83+
- **Configuration rejected:** confirm the object has only the four documented
84+
fields and the callback exactly matches the deployed HTTPS origin.
85+
- **Authorization returns an error:** compare the callback in Bitbucket with the
86+
JSON, then start **Connect** again. Authorization state expires after ten
87+
minutes and cannot be reused.
88+
- **Repositories are missing:** confirm the authorizing user has access and the
89+
consumer has Account read, Repositories write, and Pull requests write.
90+
- **Connection expires:** choose **Reconnect**. Unused Bitbucket refresh tokens
91+
expire, and provider-side revocation also requires a new authorization.
92+
- **Disconnect still appears in Bitbucket:** revoke dx from Bitbucket's app
93+
authorization settings after disconnecting locally.

‎apps/docs/src/content/docs/deployment/configuration.md‎

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Do not use a global API key. Keep account IDs and tokens out of committed config
3434

3535
v0.1.0 has no local or alternative production workspace adapter. The deployment
3636
needs an E2B API key and immutable template identities for every enabled profile.
37-
See [E2B profiles](/deployment/e2b-profiles/).
37+
See [E2B profiles](/docs/deployment/e2b-profiles/).
3838

3939
## `deploy.selfhost.json`
4040

@@ -50,7 +50,7 @@ The wizard writes this nonsecret JSON file:
5050
"zone": "example.com",
5151
"allowSignup": false,
5252
"workersAi": true,
53-
"githubCopilotClientId": "optional-public-client-id",
53+
"githubCopilotClientId": "optional-oauth-app-client-id",
5454
"modelDeploymentProviders": "optional,comma-separated,ids",
5555
"modelEndpointAllowlist": "optional,comma-separated,origins",
5656
"dxdBinary": ".dx/release/dxd-linux-x64",
@@ -75,10 +75,18 @@ The matching secret environment inputs are
7575
`DX_INTEGRATION_GITHUB_APP`, `DX_INTEGRATION_BITBUCKET_OAUTH`, and
7676
`SARVAM_API_KEY`.
7777

78-
The first wizard run asks for a hidden administrator password and confirmation;
79-
an empty first response generates one. A successful deploy stores the URL,
80-
email, and password in ignored `.dx/secrets/selfhost-admin.txt` with mode `0600`.
81-
Use `pnpm dx:deploy -- --reset-admin` to replace the password explicitly.
78+
The first self-host wizard run asks for a hidden administrator password and
79+
confirmation. An empty first response generates one. A successful deploy
80+
stores the URL, email, and password in ignored
81+
`.dx/secrets/selfhost-admin.txt` with mode `0600`. Use
82+
`pnpm dx:deploy -- --reset-admin` to replace the password explicitly.
83+
84+
The GitHub App and Bitbucket consumer values are strict JSON secrets supplied
85+
through hidden prompts or environment variables. See [GitHub source
86+
access](/docs/deployment/github-source/) and [Bitbucket source
87+
access](/docs/deployment/bitbucket-source/) for their exact schemas. The Copilot
88+
value is the client ID of an operator-owned GitHub OAuth App; see [GitHub
89+
Copilot](/docs/deployment/github-copilot/).
8290

8391
## Cost review and cancellation
8492

‎apps/docs/src/content/docs/deployment/e2b-profiles.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: E2B profiles
33
description: Size and maintain the E2B templates used by dx workspaces.
44
---
55

6-
dx v0.1.0 defines three workspace profiles:
6+
dx defines three workspace profiles:
77

88
| Profile | vCPU | Memory | Disk | Suggested use |
99
| --- | ---: | ---: | ---: | --- |
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
title: GitHub Copilot
3+
description: Configure device authorization for personal GitHub Copilot subscriptions.
4+
---
5+
6+
GitHub Copilot is an optional personal model connection. It is separate from
7+
the GitHub App used for source repositories. Each user authorizes their own
8+
Copilot subscription.
9+
10+
:::caution[Compatibility limitation]
11+
GitHub documents the OAuth device flow used for authorization, but it does not
12+
publish a supported Copilot inference API for this integration. dx currently
13+
uses compatibility endpoints and headers observed in GitHub's developer tools.
14+
GitHub can change or block that behavior. Treat Copilot as optional and keep a
15+
documented model route through a supported provider.
16+
:::
17+
18+
## Register an OAuth App
19+
20+
Create an operator-owned GitHub OAuth App. Do not copy a client ID from another
21+
application. Follow GitHub's [OAuth App registration
22+
guide](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app).
23+
24+
1. Open **Settings → Developer settings → OAuth apps → New OAuth App**.
25+
2. Set the application name and homepage URL. Use `<deployment-origin>` for the
26+
homepage.
27+
3. Set the authorization callback URL to `<deployment-origin>/`. GitHub requires
28+
this registration field, although device authorization does not redirect to
29+
it.
30+
4. Enable **Device Flow**.
31+
5. Keep expiring user access tokens enabled, then register the app.
32+
6. Copy the OAuth App's client ID. dx does not need or accept its client secret.
33+
34+
GitHub's [device flow
35+
reference](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow)
36+
documents the endpoints, polling interval, code expiry, and token refresh flow.
37+
38+
For dxcode.dev, both homepage and callback use `https://dxcode.dev`, with a
39+
trailing slash accepted for the callback field.
40+
41+
`DX_GITHUB_COPILOT_CLIENT_ID` is this OAuth App client ID. During device
42+
authorization, dx requests the documented `read:user` scope from
43+
`https://github.com/login/device/code`, directs the user to
44+
`https://github.com/login/device`, and polls
45+
`https://github.com/login/oauth/access_token`.
46+
47+
## Configure the deployment
48+
49+
Run `pnpm dx:deploy`, answer **Yes** to **Configure GitHub Copilot sign-in**, and
50+
enter the client ID at **GitHub Copilot OAuth client ID**. This value is an
51+
identifier, not a secret. Noninteractive runs set `githubCopilotClientId` in
52+
`deploy.selfhost.json`.
53+
54+
Without a nonempty client ID, Copilot authorization endpoints return
55+
unavailable and users cannot start the connection flow. Other model providers
56+
continue to work.
57+
58+
## Connect and verify
59+
60+
1. Confirm the GitHub account has an active Copilot subscription.
61+
2. Open **Settings → Model routing** in dx and choose **Connect GitHub Copilot**.
62+
3. Open the displayed `https://github.com/login/device` link and enter the
63+
one-time code.
64+
4. Return to dx and wait for the connection to complete.
65+
5. Assign an available Copilot model to one agent slot and run a small prompt.
66+
67+
dx encrypts the resulting personal OAuth credential. Refresh rechecks the
68+
GitHub identity, subscription access, and model catalog. Disconnect is blocked
69+
while a model route still uses the connection; remove those routes first.
70+
71+
## Rotate or disconnect
72+
73+
To replace the OAuth App, update `githubCopilotClientId` in
74+
`deploy.selfhost.json` and rerun `pnpm dx:deploy`. Ask users to disconnect and
75+
authorize through the replacement app, then delete the old OAuth App after
76+
verification. A user disconnects from **Settings → Model routing** after
77+
removing routes that use the subscription. They can also revoke the grant in
78+
GitHub's application settings.
79+
80+
## Troubleshooting
81+
82+
- **Connect is unavailable:** set a nonempty `githubCopilotClientId` and rerun
83+
the deployment.
84+
- **Device code fails:** confirm Device Flow is enabled on the same OAuth App
85+
whose client ID was deployed, then start a new code. Codes expire and cannot
86+
be reused.
87+
- **GitHub authorizes but no models appear:** confirm the signed-in account has
88+
Copilot access. Organization policy can restrict models.
89+
- **Inference fails after a successful connection:** the compatibility API may
90+
have changed or rejected the client. Use another configured provider; a
91+
successful OAuth grant does not prove Copilot inference compatibility.
92+
- **Disconnect is blocked:** remove every model route that uses the Copilot
93+
connection, then disconnect again.

0 commit comments

Comments
 (0)