@hanzo/build 0.2.50 → 0.2.52
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 +7 -7
- package/lib/api/github.d.ts +18 -0
- package/lib/api/github.js +19 -0
- package/lib/api/keys.d.ts +21 -13
- package/lib/api/keys.js +35 -19
- package/lib/api/work.d.ts +14 -0
- package/lib/api/work.js +17 -0
- package/lib/builder.js +17 -10
- package/lib/forge.js +175 -80
- package/lib/host.d.ts +1 -1
- package/lib/landing.js +9 -9
- package/lib/project.d.ts +16 -1
- package/lib/project.js +86 -28
- package/lib/route.d.ts +12 -4
- package/lib/route.js +8 -1
- package/lib/section.d.ts +1 -1
- package/lib/section.js +3 -3
- package/lib/settings/keys.js +22 -27
- package/lib/sync.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,18 +99,18 @@ hanzo.id token for `Host.admin`. One left column, never two.
|
|
|
99
99
|
| `-/automations` | repeating work, read from `/v1/auto/flows` |
|
|
100
100
|
| `-/customize` · `-/customize/connectors` · `/plugins` · `/agents` | Customize: skills, connectors, plugins, agents |
|
|
101
101
|
| `-/mcp` | Connectors → Discover, where the fleet's native MCP servers now live |
|
|
102
|
-
| `-/
|
|
103
|
-
| `-/sync` |
|
|
104
|
-
| `-/projects` | the forge's boards |
|
|
102
|
+
| `-/projects` | the org's projects: its repositories on the forge |
|
|
103
|
+
| `-/sync` | link repositories from GitHub onto the forge |
|
|
105
104
|
| `-/issues` | the forge's issues |
|
|
106
105
|
| `-/artifacts` | what the org has built |
|
|
107
106
|
| `-/templates` | the public starters |
|
|
108
107
|
| `-/plans` | the plans this brand sells, and the move to another |
|
|
109
108
|
| `-/settings/<section>` | Billing, Usage, Members, Integrations, Notifications, … |
|
|
110
|
-
| `<
|
|
109
|
+
| `<org>/<repo>` | a project's workspace: the repository, and its site when it has one |
|
|
110
|
+
| `<slug>` | a deployed site's workspace |
|
|
111
111
|
|
|
112
|
-
A project slug holds no `_` and never starts with `-`,
|
|
113
|
-
collide.
|
|
112
|
+
A project slug holds no `_` and never starts with `-`, and a repository is the
|
|
113
|
+
one address of two segments, so the forms cannot collide.
|
|
114
114
|
|
|
115
115
|
## What is on the screen
|
|
116
116
|
|
|
@@ -214,7 +214,7 @@ picker and open-in-tab; Share and Publish; the console dock under it.
|
|
|
214
214
|
| `PATCH /v1/agent/sessions/{id}` `{title}` · `{published}` | rename a run, share its story at `GET /v1/agent/builds/{org}/{project}` |
|
|
215
215
|
| `GET /v1/agent/sessions?kind=coding&status=&after=` | Find, by status, paged |
|
|
216
216
|
| `GET`/`POST /v1/agent/targets` · `PATCH`/`DELETE …/{id}` · `POST …/{id}/key` | the org's machines, and a machine's claim key |
|
|
217
|
-
| `GET`/`POST
|
|
217
|
+
| `GET`/`POST /v1/account/keys` · `DELETE …/{id}` | your API keys, as many as you need; one revoked at a time |
|
|
218
218
|
| `GET /v1/auto/flows` · `POST /v1/auto/flows` · `POST /v1/auto/flows/{id}/enable` | automations |
|
|
219
219
|
| `GET /v1/environment` · `GET`/`PUT`/`DELETE /v1/environment/{repo}` · `PUT`/`DELETE …/secrets/{name}` | a codebase's environment |
|
|
220
220
|
| `GET /v1/agent/coding/{session}/changes` · `/tree` · `/blob` | what a run pushed, and its branch's files, from the forge |
|
package/lib/api/github.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* POST /v1/provider/github/user/complete {grant} → the connection
|
|
9
9
|
* POST /v1/provider/github/user/disconnect
|
|
10
10
|
* GET /v1/provider/github/installations the accounts this org has bound the App on
|
|
11
|
+
* POST /v1/provider/github/issues/backfill {state, repo?} → mirror GitHub issues onto the board
|
|
11
12
|
*
|
|
12
13
|
* Paged by an opaque `after` cursor; `next` is the cursor for the page after
|
|
13
14
|
* this one, or empty on the last. Rows are read defensively: a field the
|
|
@@ -128,3 +129,20 @@ export declare function accounts(g: Grants): Account[];
|
|
|
128
129
|
export declare function grants(t: Target, signal?: AbortSignal): Promise<Grants>;
|
|
129
130
|
/** Queue a mirror of the named repositories onto the forge. The names are `owner/name`. */
|
|
130
131
|
export declare function bring(t: Target, repos: string[]): Promise<number>;
|
|
132
|
+
/** What one issue sync did. */
|
|
133
|
+
export interface Synced {
|
|
134
|
+
repos: number;
|
|
135
|
+
issues: number;
|
|
136
|
+
created: number;
|
|
137
|
+
updated: number;
|
|
138
|
+
failed: number;
|
|
139
|
+
/** The pass stopped at its budget; syncing again continues where it stopped. */
|
|
140
|
+
truncated: boolean;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Mirror GitHub issues onto the board — open and closed, so a closed one reads as
|
|
144
|
+
* closed here too. `repo` (`name` or `owner/name`) syncs one project; empty syncs
|
|
145
|
+
* every repository the connection grants. Idempotent: a second sync updates and
|
|
146
|
+
* never duplicates, and the App's webhook keeps them live afterwards.
|
|
147
|
+
*/
|
|
148
|
+
export declare function syncIssues(t: Target, repo?: string): Promise<Synced>;
|
package/lib/api/github.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* POST /v1/provider/github/user/complete {grant} → the connection
|
|
9
9
|
* POST /v1/provider/github/user/disconnect
|
|
10
10
|
* GET /v1/provider/github/installations the accounts this org has bound the App on
|
|
11
|
+
* POST /v1/provider/github/issues/backfill {state, repo?} → mirror GitHub issues onto the board
|
|
11
12
|
*
|
|
12
13
|
* Paged by an opaque `after` cursor; `next` is the cursor for the page after
|
|
13
14
|
* this one, or empty on the last. Rows are read defensively: a field the
|
|
@@ -143,3 +144,21 @@ export async function bring(t, repos) {
|
|
|
143
144
|
const raw = obj(await call(t, 'POST', '/v1/provider/github/repos/import', { repos: names }));
|
|
144
145
|
return num(raw.queued);
|
|
145
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Mirror GitHub issues onto the board — open and closed, so a closed one reads as
|
|
149
|
+
* closed here too. `repo` (`name` or `owner/name`) syncs one project; empty syncs
|
|
150
|
+
* every repository the connection grants. Idempotent: a second sync updates and
|
|
151
|
+
* never duplicates, and the App's webhook keeps them live afterwards.
|
|
152
|
+
*/
|
|
153
|
+
export async function syncIssues(t, repo = '') {
|
|
154
|
+
const name = repo.trim();
|
|
155
|
+
const raw = obj(await call(t, 'POST', '/v1/provider/github/issues/backfill', name ? { state: 'all', repo: name } : { state: 'all' }));
|
|
156
|
+
return {
|
|
157
|
+
repos: num(raw.repos),
|
|
158
|
+
issues: num(raw.issues),
|
|
159
|
+
created: num(raw.created),
|
|
160
|
+
updated: num(raw.updated),
|
|
161
|
+
failed: num(raw.failed),
|
|
162
|
+
truncated: raw.truncated === true,
|
|
163
|
+
};
|
|
164
|
+
}
|
package/lib/api/keys.d.ts
CHANGED
|
@@ -1,31 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A person's
|
|
3
|
-
*
|
|
2
|
+
* A person's API keys, each its own credential: as many as they like, of either
|
|
3
|
+
* type — secret (sk-, it belongs on a server) or publishable (pk-, safe in a
|
|
4
|
+
* page's source).
|
|
4
5
|
*
|
|
5
|
-
* GET /v1/account/keys
|
|
6
|
-
* POST /v1/account/keys
|
|
7
|
-
* DELETE /v1/account/keys
|
|
6
|
+
* GET /v1/account/keys {keys: [{id, name, type, prefix, key?, status, limit?, created, …}]}
|
|
7
|
+
* POST /v1/account/keys {name, type, limit?} → the key, its secret answered once
|
|
8
|
+
* DELETE /v1/account/keys/{id} revoke exactly that one; it stays listed as revoked
|
|
8
9
|
*
|
|
9
|
-
* Creating
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Creating a key never touches another. A secret key is answered once and by its
|
|
11
|
+
* prefix after; a publishable key is public by construction and always comes back
|
|
12
|
+
* whole. A limit only narrows what a key may reach — `model:zen5`,
|
|
13
|
+
* `project:acme`, `product:train`, `read:*`.
|
|
13
14
|
*/
|
|
14
15
|
import { type Target } from './call.ts';
|
|
15
16
|
export declare const KINDS: readonly ['secret', 'publishable'];
|
|
16
17
|
export type Kind = (typeof KINDS)[number];
|
|
17
18
|
export interface Key {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
18
21
|
type: Kind;
|
|
19
22
|
/** The recognizable head, never enough to use the key. */
|
|
20
23
|
prefix: string;
|
|
21
24
|
/** The whole key: a publishable key's only. */
|
|
22
25
|
key: string;
|
|
26
|
+
/** active, expired, revoked or disabled. */
|
|
27
|
+
status: string;
|
|
23
28
|
/** `kind:name` entries; none reaches whatever its holder does. */
|
|
24
29
|
limit: string[];
|
|
25
|
-
/** When it
|
|
30
|
+
/** When it was made, as IAM records it. */
|
|
26
31
|
created: string;
|
|
27
32
|
}
|
|
28
33
|
export interface Minted {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
29
36
|
type: Kind;
|
|
30
37
|
/** The key itself. A secret one is never readable again. */
|
|
31
38
|
key: string;
|
|
@@ -36,6 +43,7 @@ export declare function keys(t: Target): Promise<Key[]>;
|
|
|
36
43
|
export declare const LIMIT: RegExp;
|
|
37
44
|
/** The limits a person typed, split on commas and spaces; throws on the first that is not one. */
|
|
38
45
|
export declare function limits(raw: string): string[];
|
|
39
|
-
/** Create a key
|
|
40
|
-
export declare function mint(t: Target, type: Kind, limit?: string[]): Promise<Minted>;
|
|
41
|
-
|
|
46
|
+
/** Create a new key beside the ones held. The answer is the only time a secret key is shown. */
|
|
47
|
+
export declare function mint(t: Target, type: Kind, name?: string, limit?: string[]): Promise<Minted>;
|
|
48
|
+
/** Revoke exactly one key, by id. Every other key keeps working. */
|
|
49
|
+
export declare function revoke(t: Target, id: string): Promise<void>;
|
package/lib/api/keys.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A person's
|
|
3
|
-
*
|
|
2
|
+
* A person's API keys, each its own credential: as many as they like, of either
|
|
3
|
+
* type — secret (sk-, it belongs on a server) or publishable (pk-, safe in a
|
|
4
|
+
* page's source).
|
|
4
5
|
*
|
|
5
|
-
* GET /v1/account/keys
|
|
6
|
-
* POST /v1/account/keys
|
|
7
|
-
* DELETE /v1/account/keys
|
|
6
|
+
* GET /v1/account/keys {keys: [{id, name, type, prefix, key?, status, limit?, created, …}]}
|
|
7
|
+
* POST /v1/account/keys {name, type, limit?} → the key, its secret answered once
|
|
8
|
+
* DELETE /v1/account/keys/{id} revoke exactly that one; it stays listed as revoked
|
|
8
9
|
*
|
|
9
|
-
* Creating
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Creating a key never touches another. A secret key is answered once and by its
|
|
11
|
+
* prefix after; a publishable key is public by construction and always comes back
|
|
12
|
+
* whole. A limit only narrows what a key may reach — `model:zen5`,
|
|
13
|
+
* `project:acme`, `product:train`, `read:*`.
|
|
13
14
|
*/
|
|
14
|
-
import { call
|
|
15
|
+
import { call } from './call.js';
|
|
15
16
|
export const KINDS = ['secret', 'publishable'];
|
|
16
17
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
17
18
|
const list = (v) => (Array.isArray(v) ? v.filter((s) => typeof s === 'string' && s !== '') : []);
|
|
@@ -23,9 +24,18 @@ export async function keys(t) {
|
|
|
23
24
|
for (const raw of rows) {
|
|
24
25
|
const o = (raw && typeof raw === 'object' ? raw : {});
|
|
25
26
|
const type = kind(o.type);
|
|
26
|
-
if (!type)
|
|
27
|
+
if (!type || !str(o.id))
|
|
27
28
|
continue;
|
|
28
|
-
out.push({
|
|
29
|
+
out.push({
|
|
30
|
+
id: str(o.id),
|
|
31
|
+
name: str(o.name),
|
|
32
|
+
type,
|
|
33
|
+
prefix: str(o.prefix),
|
|
34
|
+
key: str(o.key),
|
|
35
|
+
status: str(o.status) || 'active',
|
|
36
|
+
limit: list(o.limit),
|
|
37
|
+
created: str(o.created),
|
|
38
|
+
});
|
|
29
39
|
}
|
|
30
40
|
return out;
|
|
31
41
|
}
|
|
@@ -42,14 +52,20 @@ export function limits(raw) {
|
|
|
42
52
|
throw new Error(`${bad} is not a limit — write it as kind:name, like model:zen5`);
|
|
43
53
|
return out;
|
|
44
54
|
}
|
|
45
|
-
/** Create a key
|
|
46
|
-
export async function mint(t, type, limit = []) {
|
|
47
|
-
const
|
|
48
|
-
|
|
55
|
+
/** Create a new key beside the ones held. The answer is the only time a secret key is shown. */
|
|
56
|
+
export async function mint(t, type, name = '', limit = []) {
|
|
57
|
+
const body = { type };
|
|
58
|
+
if (name.trim())
|
|
59
|
+
body.name = name.trim();
|
|
60
|
+
if (limit.length)
|
|
61
|
+
body.limit = limit;
|
|
62
|
+
const r = await call(t, 'POST', '/v1/account/keys', body);
|
|
63
|
+
const key = str(r?.key);
|
|
49
64
|
if (!key)
|
|
50
65
|
throw new Error('The platform answered no key');
|
|
51
|
-
return { type: kind(r?.type) ?? type, key, limit: list(r?.limit) };
|
|
66
|
+
return { id: str(r?.id), name: str(r?.name), type: kind(r?.type) ?? type, key, limit: list(r?.limit) };
|
|
52
67
|
}
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
/** Revoke exactly one key, by id. Every other key keeps working. */
|
|
69
|
+
export async function revoke(t, id) {
|
|
70
|
+
await call(t, 'DELETE', `/v1/account/keys/${encodeURIComponent(id)}`);
|
|
55
71
|
}
|
package/lib/api/work.d.ts
CHANGED
|
@@ -31,7 +31,21 @@ export interface Work {
|
|
|
31
31
|
assignee: string;
|
|
32
32
|
/** The repository the item is bound to, or ''. */
|
|
33
33
|
repo: string;
|
|
34
|
+
/** Where it was opened: `git` (the forge, or mirrored from GitHub), `team`, `agent`, … */
|
|
35
|
+
source: string;
|
|
36
|
+
labels: string[];
|
|
37
|
+
/** Its page upstream (a GitHub issue for a mirrored one), https only, or ''. */
|
|
38
|
+
url: string;
|
|
39
|
+
/** Unix milliseconds, or 0 when the platform did not say. */
|
|
40
|
+
created: number;
|
|
41
|
+
updated: number;
|
|
34
42
|
}
|
|
43
|
+
/** The project an item is filed under: its repository, else its board. */
|
|
44
|
+
export declare const projectOf: (w: Work) => string;
|
|
45
|
+
/** Done and canceled are closed; every other column is open work. */
|
|
46
|
+
export declare const closed: (w: Work) => boolean;
|
|
47
|
+
/** Whether `w` belongs to the project `key` — by repository or by board, either case. */
|
|
48
|
+
export declare function inProject(w: Work, key: string): boolean;
|
|
35
49
|
export declare function board(raw: unknown): Board | null;
|
|
36
50
|
export declare function work(raw: unknown): Work | null;
|
|
37
51
|
export declare function boards(t: Target): Promise<Board[]>;
|
package/lib/api/work.js
CHANGED
|
@@ -12,7 +12,19 @@
|
|
|
12
12
|
* reads the work and opens a run against it.
|
|
13
13
|
*/
|
|
14
14
|
import { call, seg } from './call.js';
|
|
15
|
+
/** The project an item is filed under: its repository, else its board. */
|
|
16
|
+
export const projectOf = (w) => w.repo || w.project;
|
|
17
|
+
/** Done and canceled are closed; every other column is open work. */
|
|
18
|
+
export const closed = (w) => w.status === 'done' || w.status === 'canceled';
|
|
19
|
+
/** Whether `w` belongs to the project `key` — by repository or by board, either case. */
|
|
20
|
+
export function inProject(w, key) {
|
|
21
|
+
const k = key.toLowerCase();
|
|
22
|
+
return w.repo.toLowerCase() === k || w.project.toLowerCase() === k;
|
|
23
|
+
}
|
|
15
24
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
25
|
+
/** The platform states seconds; a value already in milliseconds is kept. */
|
|
26
|
+
const when = (v) => (typeof v === 'number' && Number.isFinite(v) && v > 0 ? (v < 1e12 ? v * 1000 : v) : 0);
|
|
27
|
+
const https = (v) => (typeof v === 'string' && /^https:\/\//.test(v) ? v : '');
|
|
16
28
|
const obj = (v) => v && typeof v === 'object' ? v : {};
|
|
17
29
|
function rows(raw) {
|
|
18
30
|
if (Array.isArray(raw))
|
|
@@ -49,6 +61,11 @@ export function work(raw) {
|
|
|
49
61
|
priority: str(o.priority) || 'none',
|
|
50
62
|
assignee: str(o.assignee),
|
|
51
63
|
repo: str(o.repo),
|
|
64
|
+
source: str(o.source),
|
|
65
|
+
labels: Array.isArray(o.labels) ? o.labels.filter((l) => typeof l === 'string' && l !== '') : [],
|
|
66
|
+
url: https(o.url) || https(o.extRef),
|
|
67
|
+
created: when(o.createdAt),
|
|
68
|
+
updated: when(o.updatedAt),
|
|
52
69
|
};
|
|
53
70
|
}
|
|
54
71
|
export async function boards(t) {
|
package/lib/builder.js
CHANGED
|
@@ -5,14 +5,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
* / New — the empty state, a composer over the codebase it works on
|
|
6
6
|
* /sess_… one run, live
|
|
7
7
|
* /-/automations work that runs itself; /-/automations/<id> one, to edit and run
|
|
8
|
-
/-/
|
|
9
|
-
* /-/sync
|
|
10
|
-
* /-/projects the forge's boards
|
|
8
|
+
* /-/projects the org's repositories on the forge: a project is a repository
|
|
9
|
+
* /-/sync link repositories from GitHub onto the forge
|
|
11
10
|
* /-/issues the forge's issues
|
|
12
11
|
* /-/artifacts what this org has built
|
|
13
12
|
* /-/templates the public starters
|
|
14
13
|
* /-/customize skills, connectors, plugins and agents
|
|
15
|
-
* /<
|
|
14
|
+
* /<org>/<repo> a repository's workspace, in this same window
|
|
15
|
+
* /<slug> a deployed site's workspace
|
|
16
16
|
*
|
|
17
17
|
* The rail is the sessions rail every Hanzo surface shares: New, the builder's
|
|
18
18
|
* places with the rest under More, the org's coding runs newest first with a
|
|
@@ -20,15 +20,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
20
20
|
* whose menu opens Settings, Usage, the plans, help and logging out (foot.tsx).
|
|
21
21
|
* Its collapse is an explicit toggle kept per browser.
|
|
22
22
|
*
|
|
23
|
-
* A
|
|
24
|
-
*
|
|
23
|
+
* A workspace takes the whole window — it has its own chat column, bar and
|
|
24
|
+
* panes — and its mark leads back here.
|
|
25
25
|
*
|
|
26
26
|
* A host with a rail of its own mounts `<Builder rail={false}>` and draws the
|
|
27
27
|
* builder's places (`nav`) and runs in that rail (`useSessions`, or `DevSection`,
|
|
28
28
|
* section.tsx): one left column, never two.
|
|
29
29
|
*/
|
|
30
30
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
31
|
-
import { Blocks, BookOpen, CircleDot, Cpu, FolderGit2,
|
|
31
|
+
import { Blocks, BookOpen, CircleDot, Cpu, FolderGit2, LayoutTemplate, Menu, Search, SlidersHorizontal, Workflow } from '@hanzogui/lucide-icons-2';
|
|
32
32
|
import { Button } from '@hanzo/ui';
|
|
33
33
|
import { SessionRail } from '@hanzo/ui/chat';
|
|
34
34
|
import { HanzoMark } from '@hanzo/ui/product';
|
|
@@ -98,13 +98,18 @@ export function nav(host, go) {
|
|
|
98
98
|
const r = route(host.path);
|
|
99
99
|
const screen = r.kind === 'screen' ? r.screen : '';
|
|
100
100
|
const links = [
|
|
101
|
-
{
|
|
101
|
+
{
|
|
102
|
+
id: 'projects',
|
|
103
|
+
label: 'Projects',
|
|
104
|
+
icon: _jsx(FolderGit2, { size: 16 }),
|
|
105
|
+
onPress: () => go(path({ kind: 'screen', screen: 'projects' })),
|
|
106
|
+
active: screen === 'projects' || screen === 'sync' || r.kind === 'repo',
|
|
107
|
+
},
|
|
102
108
|
{ id: 'artifacts', label: 'Artifacts', icon: _jsx(Blocks, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'artifacts' })), active: screen === 'artifacts' },
|
|
103
109
|
{ id: 'customize', label: 'Customize', icon: _jsx(SlidersHorizontal, { size: 16 }), onPress: () => go(path({ kind: 'customize', tab: 'skills' })), active: r.kind === 'customize' || screen === 'mcp' },
|
|
104
110
|
{ id: 'automations', label: 'Automations', icon: _jsx(Workflow, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'automations' })), active: screen === 'automations' || r.kind === 'automation' },
|
|
105
111
|
];
|
|
106
112
|
const more = [
|
|
107
|
-
{ id: 'codebases', label: 'Codebase', icon: _jsx(FolderGit2, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'codebases' })), active: screen === 'codebases' },
|
|
108
113
|
{
|
|
109
114
|
id: 'issues',
|
|
110
115
|
label: 'Issues',
|
|
@@ -172,7 +177,9 @@ function Screens({ rail }) {
|
|
|
172
177
|
const host = useHost();
|
|
173
178
|
const r = route(host.path);
|
|
174
179
|
if (r.kind === 'project')
|
|
175
|
-
return _jsx(Project, { slug: r.slug }, r.slug);
|
|
180
|
+
return _jsx(Project, { at: { slug: r.slug } }, r.slug);
|
|
181
|
+
if (r.kind === 'repo')
|
|
182
|
+
return _jsx(Project, { at: { org: r.org, name: r.name } }, `${r.org}/${r.name}`);
|
|
176
183
|
if (!rail)
|
|
177
184
|
return _jsx(Pane, {});
|
|
178
185
|
return _jsx(Shell, {});
|