@hanzo/build 0.2.9 → 0.2.11
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 +126 -23
- package/lib/api/agents.d.ts +80 -0
- package/lib/api/agents.js +148 -0
- package/lib/api/billing.d.ts +205 -0
- package/lib/api/billing.js +300 -0
- package/lib/api/call.d.ts +6 -0
- package/lib/api/call.js +11 -0
- package/lib/api/capabilities.d.ts +25 -0
- package/lib/api/capabilities.js +40 -0
- package/lib/api/changes.d.ts +56 -0
- package/lib/api/changes.js +63 -0
- package/lib/api/coding.d.ts +42 -4
- package/lib/api/coding.js +62 -3
- package/lib/api/connectors.d.ts +90 -0
- package/lib/api/connectors.js +109 -0
- package/lib/api/consent.d.ts +22 -0
- package/lib/api/consent.js +28 -0
- package/lib/api/environment.d.ts +57 -0
- package/lib/api/environment.js +85 -0
- package/lib/api/git.d.ts +6 -2
- package/lib/api/git.js +13 -5
- package/lib/api/github.d.ts +13 -0
- package/lib/api/github.js +9 -0
- package/lib/api/harness.d.ts +44 -0
- package/lib/api/harness.js +348 -0
- package/lib/api/keys.d.ts +41 -0
- package/lib/api/keys.js +55 -0
- package/lib/api/machines.d.ts +56 -0
- package/lib/api/machines.js +84 -0
- package/lib/api/members.d.ts +47 -0
- package/lib/api/members.js +88 -0
- package/lib/api/memory.d.ts +25 -0
- package/lib/api/memory.js +34 -0
- package/lib/api/places.d.ts +2 -2
- package/lib/api/places.js +3 -26
- package/lib/api/plugins.d.ts +55 -0
- package/lib/api/plugins.js +62 -0
- package/lib/api/pref.d.ts +49 -0
- package/lib/api/pref.js +68 -0
- package/lib/api/profile.d.ts +21 -0
- package/lib/api/profile.js +40 -0
- package/lib/api/projects.d.ts +19 -2
- package/lib/api/projects.js +29 -4
- package/lib/api/provider.d.ts +42 -0
- package/lib/api/provider.js +58 -0
- package/lib/api/sandbox.d.ts +30 -0
- package/lib/api/sandbox.js +49 -0
- package/lib/api/sessions.d.ts +39 -4
- package/lib/api/sessions.js +58 -6
- package/lib/api/skills.d.ts +70 -0
- package/lib/api/skills.js +90 -0
- package/lib/api/tools.d.ts +34 -0
- package/lib/api/tools.js +37 -0
- package/lib/api/turn.d.ts +84 -16
- package/lib/api/turn.js +224 -4
- package/lib/api/webhooks.d.ts +60 -0
- package/lib/api/webhooks.js +81 -0
- package/lib/ask.d.ts +18 -0
- package/lib/ask.js +69 -0
- package/lib/builder.js +39 -19
- package/lib/customize/agents.d.ts +2 -0
- package/lib/customize/agents.js +125 -0
- package/lib/customize/connectors.d.ts +2 -0
- package/lib/customize/connectors.js +197 -0
- package/lib/customize/index.d.ts +6 -0
- package/lib/customize/index.js +50 -0
- package/lib/customize/plugins.d.ts +2 -0
- package/lib/customize/plugins.js +93 -0
- package/lib/customize/skills.d.ts +2 -0
- package/lib/customize/skills.js +125 -0
- package/lib/customize/ui.d.ts +110 -0
- package/lib/customize/ui.js +99 -0
- package/lib/desk.d.ts +8 -1
- package/lib/desk.js +29 -35
- package/lib/door.d.ts +7 -0
- package/lib/door.js +89 -0
- package/lib/environment.d.ts +15 -0
- package/lib/environment.js +132 -0
- package/lib/files.d.ts +14 -0
- package/lib/files.js +111 -0
- package/lib/find.d.ts +7 -0
- package/lib/find.js +77 -0
- package/lib/foot.d.ts +16 -0
- package/lib/foot.js +36 -0
- package/lib/forge.js +8 -4
- package/lib/git.d.ts +9 -0
- package/lib/git.js +74 -0
- package/lib/host.d.ts +5 -0
- package/lib/landing.d.ts +10 -0
- package/lib/landing.js +47 -14
- package/lib/markdown.d.ts +49 -0
- package/lib/markdown.js +123 -0
- package/lib/plans.d.ts +1 -0
- package/lib/plans.js +113 -0
- package/lib/prefs.d.ts +30 -0
- package/lib/prefs.js +83 -0
- package/lib/prose.d.ts +4 -0
- package/lib/prose.js +53 -0
- package/lib/route.d.ts +16 -1
- package/lib/route.js +32 -1
- package/lib/run.js +207 -28
- package/lib/section.js +2 -2
- package/lib/settings/account.d.ts +5 -0
- package/lib/settings/account.js +111 -0
- package/lib/settings/billing.d.ts +1 -0
- package/lib/settings/billing.js +104 -0
- package/lib/settings/capabilities.d.ts +1 -0
- package/lib/settings/capabilities.js +55 -0
- package/lib/settings/card.d.ts +31 -0
- package/lib/settings/card.js +105 -0
- package/lib/settings/code.d.ts +1 -0
- package/lib/settings/code.js +51 -0
- package/lib/settings/environments.d.ts +1 -0
- package/lib/settings/environments.js +53 -0
- package/lib/settings/general.d.ts +1 -0
- package/lib/settings/general.js +44 -0
- package/lib/settings/index.d.ts +4 -0
- package/lib/settings/index.js +22 -0
- package/lib/settings/integrations.d.ts +1 -0
- package/lib/settings/integrations.js +81 -0
- package/lib/settings/keys.d.ts +1 -0
- package/lib/settings/keys.js +102 -0
- package/lib/settings/machines.d.ts +1 -0
- package/lib/settings/machines.js +118 -0
- package/lib/settings/members.d.ts +1 -0
- package/lib/settings/members.js +67 -0
- package/lib/settings/memory.d.ts +1 -0
- package/lib/settings/memory.js +56 -0
- package/lib/settings/notifications.d.ts +1 -0
- package/lib/settings/notifications.js +97 -0
- package/lib/settings/privacy.d.ts +1 -0
- package/lib/settings/privacy.js +63 -0
- package/lib/settings/sections.d.ts +16 -0
- package/lib/settings/sections.js +32 -0
- package/lib/settings/ui.d.ts +49 -0
- package/lib/settings/ui.js +47 -0
- package/lib/settings/usage.d.ts +1 -0
- package/lib/settings/usage.js +114 -0
- package/lib/shelf.d.ts +6 -0
- package/lib/shelf.js +86 -16
- package/lib/switch.js +1 -2
- package/lib/transcript.d.ts +12 -0
- package/lib/transcript.js +56 -0
- package/lib/voice.d.ts +3 -1
- package/lib/voice.js +10 -4
- package/package.json +1 -1
- package/lib/account.d.ts +0 -14
- package/lib/account.js +0 -38
- package/lib/e2e/site.spec.d.ts +0 -1
- package/lib/e2e/site.spec.js +0 -125
- package/lib/mcp.d.ts +0 -1
- package/lib/mcp.js +0 -28
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connectors: the MCP servers an org adds, and the shelf it picks them from.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/tool/mcp/servers {servers: [server]}
|
|
5
|
+
* POST /v1/tool/mcp/servers {name, url} or {listing, name?}, with {authHeader, secret} → 201 server
|
|
6
|
+
* DELETE /v1/tool/mcp/servers/{id} 204
|
|
7
|
+
* GET /v1/tool/catalog?q=&limit=&offset= {catalog: [listing], total, limit, offset}
|
|
8
|
+
* GET /v1/tool/catalog/{id} one listing in full
|
|
9
|
+
*
|
|
10
|
+
* A server's secret is sealed in KMS by the platform and never answered again;
|
|
11
|
+
* a record says only whether it has one. Its tools are GET /v1/tool?source=mcp
|
|
12
|
+
* (tools.ts), named `<server id>_<tool>`, and each is called only once it is on.
|
|
13
|
+
* A listing can be added here only when it serves streamable HTTP; one that
|
|
14
|
+
* ships only a package needs somewhere to run first. The fleet's own servers,
|
|
15
|
+
* on for every run, are mcp.ts.
|
|
16
|
+
*/
|
|
17
|
+
import { type Target } from './call.ts';
|
|
18
|
+
export interface Server {
|
|
19
|
+
/** The server's id in the org, and the prefix of every tool it brings. */
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
url: string;
|
|
23
|
+
/** The header the sealed secret is sent in, or ''. */
|
|
24
|
+
header: string;
|
|
25
|
+
/** Whether a secret is sealed for it. */
|
|
26
|
+
secret: boolean;
|
|
27
|
+
/** The catalog listing it was added from, or '' for a URL typed in. */
|
|
28
|
+
listing: string;
|
|
29
|
+
created: number;
|
|
30
|
+
}
|
|
31
|
+
export interface Remote {
|
|
32
|
+
transport: string;
|
|
33
|
+
url: string;
|
|
34
|
+
}
|
|
35
|
+
export interface Package {
|
|
36
|
+
registry: string;
|
|
37
|
+
identifier: string;
|
|
38
|
+
runtime: string;
|
|
39
|
+
version: string;
|
|
40
|
+
}
|
|
41
|
+
export interface Listing {
|
|
42
|
+
id: string;
|
|
43
|
+
/** The publisher's reverse-DNS name, `com.stripe/mcp`. */
|
|
44
|
+
name: string;
|
|
45
|
+
title: string;
|
|
46
|
+
description: string;
|
|
47
|
+
vendor: string;
|
|
48
|
+
version: string;
|
|
49
|
+
logo: string;
|
|
50
|
+
featured: boolean;
|
|
51
|
+
official: boolean;
|
|
52
|
+
transports: string[];
|
|
53
|
+
remotes: Remote[];
|
|
54
|
+
packages: Package[];
|
|
55
|
+
repo: string;
|
|
56
|
+
site: string;
|
|
57
|
+
}
|
|
58
|
+
export interface Page {
|
|
59
|
+
listings: Listing[];
|
|
60
|
+
total: number;
|
|
61
|
+
offset: number;
|
|
62
|
+
}
|
|
63
|
+
export declare function server(raw: unknown): Server;
|
|
64
|
+
export declare function listing(raw: unknown): Listing;
|
|
65
|
+
/** What a listing is called on screen. */
|
|
66
|
+
export declare const titleOf: (l: Listing) => string;
|
|
67
|
+
/** Whether a listing can be added here and now: the platform dials its streamable-HTTP remote. */
|
|
68
|
+
export declare const ready: (l: Listing) => boolean;
|
|
69
|
+
/** A server's tools among the plane's, by the prefix its id gives them. */
|
|
70
|
+
export declare const owns: (s: Server, toolName: string) => boolean;
|
|
71
|
+
export declare function servers(t: Target): Promise<Server[]>;
|
|
72
|
+
export interface Adding {
|
|
73
|
+
/** Required with a URL; a listing takes its own title when this is empty. */
|
|
74
|
+
name?: string;
|
|
75
|
+
url?: string;
|
|
76
|
+
listing?: string;
|
|
77
|
+
header?: string;
|
|
78
|
+
secret?: string;
|
|
79
|
+
}
|
|
80
|
+
/** Why a server cannot be added as it stands, or '' when it can. */
|
|
81
|
+
export declare function refuse(a: Adding): string;
|
|
82
|
+
export declare function add(t: Target, a: Adding): Promise<Server>;
|
|
83
|
+
export declare function remove(t: Target, id: string): Promise<void>;
|
|
84
|
+
/** A page of the shelf, featured first, then by name. */
|
|
85
|
+
export declare function shelf(t: Target, q?: {
|
|
86
|
+
text?: string;
|
|
87
|
+
limit?: number;
|
|
88
|
+
offset?: number;
|
|
89
|
+
}): Promise<Page>;
|
|
90
|
+
export declare function one(t: Target, id: string): Promise<Listing>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connectors: the MCP servers an org adds, and the shelf it picks them from.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/tool/mcp/servers {servers: [server]}
|
|
5
|
+
* POST /v1/tool/mcp/servers {name, url} or {listing, name?}, with {authHeader, secret} → 201 server
|
|
6
|
+
* DELETE /v1/tool/mcp/servers/{id} 204
|
|
7
|
+
* GET /v1/tool/catalog?q=&limit=&offset= {catalog: [listing], total, limit, offset}
|
|
8
|
+
* GET /v1/tool/catalog/{id} one listing in full
|
|
9
|
+
*
|
|
10
|
+
* A server's secret is sealed in KMS by the platform and never answered again;
|
|
11
|
+
* a record says only whether it has one. Its tools are GET /v1/tool?source=mcp
|
|
12
|
+
* (tools.ts), named `<server id>_<tool>`, and each is called only once it is on.
|
|
13
|
+
* A listing can be added here only when it serves streamable HTTP; one that
|
|
14
|
+
* ships only a package needs somewhere to run first. The fleet's own servers,
|
|
15
|
+
* on for every run, are mcp.ts.
|
|
16
|
+
*/
|
|
17
|
+
import { call, query, seg } from './call.js';
|
|
18
|
+
const str = (v) => (typeof v === 'string' ? v : '');
|
|
19
|
+
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
20
|
+
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
21
|
+
const rows = (v) => (Array.isArray(v) ? v : []);
|
|
22
|
+
export function server(raw) {
|
|
23
|
+
const o = obj(raw);
|
|
24
|
+
return {
|
|
25
|
+
id: str(o.id),
|
|
26
|
+
name: str(o.name),
|
|
27
|
+
url: str(o.url),
|
|
28
|
+
header: str(o.authHeader),
|
|
29
|
+
secret: o.hasSecret === true,
|
|
30
|
+
listing: str(o.listing),
|
|
31
|
+
created: num(o.createdAt),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function listing(raw) {
|
|
35
|
+
const o = obj(raw);
|
|
36
|
+
return {
|
|
37
|
+
id: str(o.id),
|
|
38
|
+
name: str(o.name),
|
|
39
|
+
title: str(o.title),
|
|
40
|
+
description: str(o.description),
|
|
41
|
+
vendor: str(o.vendor),
|
|
42
|
+
version: str(o.version),
|
|
43
|
+
logo: str(o.logo),
|
|
44
|
+
featured: o.featured === true,
|
|
45
|
+
official: o.official === true,
|
|
46
|
+
transports: rows(o.transports).map(str).filter(Boolean),
|
|
47
|
+
remotes: rows(o.remotes)
|
|
48
|
+
.map((r) => ({ transport: str(obj(r).transport), url: str(obj(r).url) }))
|
|
49
|
+
.filter((r) => r.url),
|
|
50
|
+
packages: rows(o.packages)
|
|
51
|
+
.map((p) => {
|
|
52
|
+
const x = obj(p);
|
|
53
|
+
return { registry: str(x.registry), identifier: str(x.identifier), runtime: str(x.runtime), version: str(x.version) };
|
|
54
|
+
})
|
|
55
|
+
.filter((p) => p.identifier),
|
|
56
|
+
repo: str(o.repo),
|
|
57
|
+
site: str(o.site),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** What a listing is called on screen. */
|
|
61
|
+
export const titleOf = (l) => l.title || l.name;
|
|
62
|
+
/** Whether a listing can be added here and now: the platform dials its streamable-HTTP remote. */
|
|
63
|
+
export const ready = (l) => l.remotes.some((r) => r.transport === 'streamable-http');
|
|
64
|
+
/** A server's tools among the plane's, by the prefix its id gives them. */
|
|
65
|
+
export const owns = (s, toolName) => toolName.startsWith(`${s.id}_`);
|
|
66
|
+
export async function servers(t) {
|
|
67
|
+
return rows(obj(await call(t, 'GET', '/v1/tool/mcp/servers')).servers).map(server).filter((s) => s.id);
|
|
68
|
+
}
|
|
69
|
+
/** Why a server cannot be added as it stands, or '' when it can. */
|
|
70
|
+
export function refuse(a) {
|
|
71
|
+
if (!a.listing) {
|
|
72
|
+
if (!a.name?.trim())
|
|
73
|
+
return 'A connector needs a name';
|
|
74
|
+
if (!/^https?:\/\/[^\s/]+/.test(a.url?.trim() ?? ''))
|
|
75
|
+
return 'The URL is an http(s) address';
|
|
76
|
+
}
|
|
77
|
+
if (a.secret && !a.header?.trim())
|
|
78
|
+
return 'Name the header the secret is sent in';
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
export async function add(t, a) {
|
|
82
|
+
const why = refuse(a);
|
|
83
|
+
if (why)
|
|
84
|
+
throw new Error(why);
|
|
85
|
+
const body = {};
|
|
86
|
+
if (a.listing)
|
|
87
|
+
body.listing = a.listing;
|
|
88
|
+
else
|
|
89
|
+
body.url = a.url.trim();
|
|
90
|
+
if (a.name?.trim())
|
|
91
|
+
body.name = a.name.trim();
|
|
92
|
+
// The header names where the secret goes, so it rides with one or not at all.
|
|
93
|
+
if (a.secret) {
|
|
94
|
+
body.authHeader = a.header.trim();
|
|
95
|
+
body.secret = a.secret;
|
|
96
|
+
}
|
|
97
|
+
return server(await call(t, 'POST', '/v1/tool/mcp/servers', body));
|
|
98
|
+
}
|
|
99
|
+
export async function remove(t, id) {
|
|
100
|
+
await call(t, 'DELETE', `/v1/tool/mcp/servers/${seg(id)}`);
|
|
101
|
+
}
|
|
102
|
+
/** A page of the shelf, featured first, then by name. */
|
|
103
|
+
export async function shelf(t, q = {}) {
|
|
104
|
+
const r = obj(await call(t, 'GET', `/v1/tool/catalog${query({ q: q.text?.trim(), limit: q.limit, offset: q.offset || undefined })}`));
|
|
105
|
+
return { listings: rows(r.catalog).map(listing).filter((l) => l.id), total: num(r.total), offset: num(r.offset) };
|
|
106
|
+
}
|
|
107
|
+
export async function one(t, id) {
|
|
108
|
+
return listing(await call(t, 'GET', `/v1/tool/catalog/${seg(id)}`));
|
|
109
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signed-in person's answers to Hanzo's data-sharing questions, as IAM
|
|
3
|
+
* records them for every Hanzo surface.
|
|
4
|
+
*
|
|
5
|
+
* GET /v1/iam/consent → {status, data: {insights, training}}
|
|
6
|
+
* PUT /v1/iam/consent {insights?, training?} only the answers sent change
|
|
7
|
+
*
|
|
8
|
+
* `training` has three states: unanswered (''), granted, refused. Only granted
|
|
9
|
+
* lets Hanzo train on this person's data; unanswered is read as no. `insights`
|
|
10
|
+
* is anonymous product usage, with no prompt or answer text, and is on until
|
|
11
|
+
* turned off. Every change is audited by IAM with the answer before and after.
|
|
12
|
+
*/
|
|
13
|
+
import { type Target } from './call.ts';
|
|
14
|
+
export type Answer = '' | 'granted' | 'refused';
|
|
15
|
+
export interface Consent {
|
|
16
|
+
insights: boolean;
|
|
17
|
+
training: Answer;
|
|
18
|
+
}
|
|
19
|
+
/** IAM's defaults for anything it did not say: insights on, training unanswered. */
|
|
20
|
+
export declare function consentOf(raw: unknown): Consent;
|
|
21
|
+
export declare function consent(t: Target): Promise<Consent>;
|
|
22
|
+
export declare function setConsent(t: Target, change: Partial<Consent>): Promise<Consent>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The signed-in person's answers to Hanzo's data-sharing questions, as IAM
|
|
3
|
+
* records them for every Hanzo surface.
|
|
4
|
+
*
|
|
5
|
+
* GET /v1/iam/consent → {status, data: {insights, training}}
|
|
6
|
+
* PUT /v1/iam/consent {insights?, training?} only the answers sent change
|
|
7
|
+
*
|
|
8
|
+
* `training` has three states: unanswered (''), granted, refused. Only granted
|
|
9
|
+
* lets Hanzo train on this person's data; unanswered is read as no. `insights`
|
|
10
|
+
* is anonymous product usage, with no prompt or answer text, and is on until
|
|
11
|
+
* turned off. Every change is audited by IAM with the answer before and after.
|
|
12
|
+
*/
|
|
13
|
+
import { call, unwrap } from './call.js';
|
|
14
|
+
const ANSWERS = ['', 'granted', 'refused'];
|
|
15
|
+
/** IAM's defaults for anything it did not say: insights on, training unanswered. */
|
|
16
|
+
export function consentOf(raw) {
|
|
17
|
+
const o = (raw && typeof raw === 'object' ? raw : {});
|
|
18
|
+
return {
|
|
19
|
+
insights: typeof o.insights === 'boolean' ? o.insights : true,
|
|
20
|
+
training: ANSWERS.includes(o.training) ? o.training : '',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export async function consent(t) {
|
|
24
|
+
return consentOf(unwrap(await call(t, 'GET', '/v1/iam/consent')));
|
|
25
|
+
}
|
|
26
|
+
export async function setConsent(t, change) {
|
|
27
|
+
return consentOf(unwrap(await call(t, 'PUT', '/v1/iam/consent', change)));
|
|
28
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A codebase's environment: what a run does to its checkout before the agent
|
|
3
|
+
* starts, and which secrets it exports.
|
|
4
|
+
*
|
|
5
|
+
* GET /v1/environment {data: [environment]}
|
|
6
|
+
* GET /v1/environment/{repo} one; a codebase with none answers state "none"
|
|
7
|
+
* PUT /v1/environment/{repo} save {install, start} (org admin)
|
|
8
|
+
* DELETE /v1/environment/{repo} forget it and its secrets (org admin)
|
|
9
|
+
* PUT /v1/environment/{repo}/secrets/{name} set one value (org admin)
|
|
10
|
+
* DELETE /v1/environment/{repo}/secrets/{name} remove one (org admin)
|
|
11
|
+
*
|
|
12
|
+
* A setup run is a coding run in mode `setup` (coding.ts): its agent explores the
|
|
13
|
+
* checkout, installs and checks what it finds, and the platform keeps its answer
|
|
14
|
+
* here as `proposal` until someone saves it. Secrets are names only; their values
|
|
15
|
+
* are sealed in KMS and reach the run's environment, never this record.
|
|
16
|
+
*/
|
|
17
|
+
import { type Target } from './call.ts';
|
|
18
|
+
export type State = 'none' | 'proposed' | 'ready';
|
|
19
|
+
export interface Proposal {
|
|
20
|
+
install: string;
|
|
21
|
+
start: string;
|
|
22
|
+
/** Variables the agent found the codebase reads. Some may not be set yet. */
|
|
23
|
+
secrets: string[];
|
|
24
|
+
/** What the setup agent found and checked, in its own words. */
|
|
25
|
+
note: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Environment {
|
|
28
|
+
repo: string;
|
|
29
|
+
install: string;
|
|
30
|
+
start: string;
|
|
31
|
+
/** The names set on this codebase. Values are never answered. */
|
|
32
|
+
secrets: string[];
|
|
33
|
+
state: State;
|
|
34
|
+
/** The setup run that produced the proposal, or ''. */
|
|
35
|
+
session: string;
|
|
36
|
+
proposal: Proposal | null;
|
|
37
|
+
updated: string;
|
|
38
|
+
}
|
|
39
|
+
export declare function environment(raw: unknown, repo?: string): Environment;
|
|
40
|
+
export declare function environments(t: Target): Promise<Environment[]>;
|
|
41
|
+
export declare function read(t: Target, repo: string): Promise<Environment>;
|
|
42
|
+
export declare function save(t: Target, repo: string, scripts: {
|
|
43
|
+
install: string;
|
|
44
|
+
start: string;
|
|
45
|
+
}): Promise<Environment>;
|
|
46
|
+
export declare function forget(t: Target, repo: string): Promise<void>;
|
|
47
|
+
/** An environment variable name the run can export, and not one of its own. */
|
|
48
|
+
export declare const NAME: RegExp;
|
|
49
|
+
/** Why a secret name cannot be set, or '' when it can. */
|
|
50
|
+
export declare function refuse(name: string): string;
|
|
51
|
+
export declare function setSecret(t: Target, repo: string, name: string, value: string): Promise<Environment>;
|
|
52
|
+
export declare function removeSecret(t: Target, repo: string, name: string): Promise<Environment>;
|
|
53
|
+
/**
|
|
54
|
+
* What a setup run is asked. The first line is the run's title, the steps are
|
|
55
|
+
* its checklist, and the platform puts its own instructions ahead of both.
|
|
56
|
+
*/
|
|
57
|
+
export declare const SETUP: string;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A codebase's environment: what a run does to its checkout before the agent
|
|
3
|
+
* starts, and which secrets it exports.
|
|
4
|
+
*
|
|
5
|
+
* GET /v1/environment {data: [environment]}
|
|
6
|
+
* GET /v1/environment/{repo} one; a codebase with none answers state "none"
|
|
7
|
+
* PUT /v1/environment/{repo} save {install, start} (org admin)
|
|
8
|
+
* DELETE /v1/environment/{repo} forget it and its secrets (org admin)
|
|
9
|
+
* PUT /v1/environment/{repo}/secrets/{name} set one value (org admin)
|
|
10
|
+
* DELETE /v1/environment/{repo}/secrets/{name} remove one (org admin)
|
|
11
|
+
*
|
|
12
|
+
* A setup run is a coding run in mode `setup` (coding.ts): its agent explores the
|
|
13
|
+
* checkout, installs and checks what it finds, and the platform keeps its answer
|
|
14
|
+
* here as `proposal` until someone saves it. Secrets are names only; their values
|
|
15
|
+
* are sealed in KMS and reach the run's environment, never this record.
|
|
16
|
+
*/
|
|
17
|
+
import { call, seg } from './call.js';
|
|
18
|
+
const str = (v) => (typeof v === 'string' ? v : '');
|
|
19
|
+
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
20
|
+
const names = (v) => (Array.isArray(v) ? v.filter((n) => typeof n === 'string' && n !== '') : []);
|
|
21
|
+
const STATES = ['none', 'proposed', 'ready'];
|
|
22
|
+
export function environment(raw, repo = '') {
|
|
23
|
+
const o = obj(raw);
|
|
24
|
+
const p = o.proposal ? obj(o.proposal) : null;
|
|
25
|
+
const state = str(o.state);
|
|
26
|
+
return {
|
|
27
|
+
repo: str(o.repo) || repo,
|
|
28
|
+
install: str(o.install),
|
|
29
|
+
start: str(o.start),
|
|
30
|
+
secrets: names(o.secrets),
|
|
31
|
+
state: STATES.includes(state) ? state : 'none',
|
|
32
|
+
session: str(o.session),
|
|
33
|
+
proposal: p ? { install: str(p.install), start: str(p.start), secrets: names(p.secrets), note: str(p.note) } : null,
|
|
34
|
+
updated: str(o.updatedAt),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function environments(t) {
|
|
38
|
+
const r = await call(t, 'GET', '/v1/environment');
|
|
39
|
+
return (Array.isArray(r?.data) ? r.data : []).map((e) => environment(e)).filter((e) => e.repo);
|
|
40
|
+
}
|
|
41
|
+
export async function read(t, repo) {
|
|
42
|
+
return environment(await call(t, 'GET', `/v1/environment/${seg(repo)}`), repo);
|
|
43
|
+
}
|
|
44
|
+
export async function save(t, repo, scripts) {
|
|
45
|
+
return environment(await call(t, 'PUT', `/v1/environment/${seg(repo)}`, scripts), repo);
|
|
46
|
+
}
|
|
47
|
+
export async function forget(t, repo) {
|
|
48
|
+
await call(t, 'DELETE', `/v1/environment/${seg(repo)}`);
|
|
49
|
+
}
|
|
50
|
+
/** An environment variable name the run can export, and not one of its own. */
|
|
51
|
+
export const NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
52
|
+
const RESERVED = ['PATH', 'HOME', 'USER', 'SHELL', 'PWD', 'HOSTNAME', 'IFS', 'ENV', 'BASH_ENV'];
|
|
53
|
+
/** Why a secret name cannot be set, or '' when it can. */
|
|
54
|
+
export function refuse(name) {
|
|
55
|
+
if (!NAME.test(name))
|
|
56
|
+
return `${name || 'That'} is not an environment variable name`;
|
|
57
|
+
const up = name.toUpperCase();
|
|
58
|
+
if (up.startsWith('HANZO_') || RESERVED.includes(up))
|
|
59
|
+
return `${name} is reserved for the run itself`;
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
62
|
+
export async function setSecret(t, repo, name, value) {
|
|
63
|
+
const why = refuse(name);
|
|
64
|
+
if (why)
|
|
65
|
+
throw new Error(why);
|
|
66
|
+
if (!value)
|
|
67
|
+
throw new Error('A secret needs a value');
|
|
68
|
+
return environment(await call(t, 'PUT', `/v1/environment/${seg(repo)}/secrets/${seg(name)}`, { value }), repo);
|
|
69
|
+
}
|
|
70
|
+
export async function removeSecret(t, repo, name) {
|
|
71
|
+
return environment(await call(t, 'DELETE', `/v1/environment/${seg(repo)}/secrets/${seg(name)}`), repo);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* What a setup run is asked. The first line is the run's title, the steps are
|
|
75
|
+
* its checklist, and the platform puts its own instructions ahead of both.
|
|
76
|
+
*/
|
|
77
|
+
export const SETUP = [
|
|
78
|
+
'Set up the environment',
|
|
79
|
+
'',
|
|
80
|
+
'1. Understand the codebase: how it installs, builds, starts and tests.',
|
|
81
|
+
'2. Write the install script and the start command.',
|
|
82
|
+
'3. Run both in this fresh checkout.',
|
|
83
|
+
'4. Check that the build and the tests pass, and that the app answers if it is one.',
|
|
84
|
+
'5. Answer with what you checked and the environment.',
|
|
85
|
+
].join('\n');
|
package/lib/api/git.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* GET /v1/git/repos/{name}/blob?ref=&path= one file; binary is base64,
|
|
6
6
|
* past 1 MiB it is truncated with no content
|
|
7
7
|
*
|
|
8
|
-
* A run pushes its branch to
|
|
9
|
-
*
|
|
8
|
+
* A run pushes its branch to the forge, not here, so a run's files are read
|
|
9
|
+
* through the run (changes.ts); these read a codebase as the platform holds it.
|
|
10
10
|
*/
|
|
11
11
|
import { type Target } from './call.ts';
|
|
12
12
|
export interface Entry {
|
|
@@ -24,4 +24,8 @@ export interface Blob {
|
|
|
24
24
|
size: number;
|
|
25
25
|
}
|
|
26
26
|
export declare function tree(t: Target, repo: string, ref: string, path?: string): Promise<Entry[]>;
|
|
27
|
+
/** A tree answer's entries. A run's branch on the forge answers in the same shape. */
|
|
28
|
+
export declare function entries(raw: unknown): Entry[];
|
|
27
29
|
export declare function blob(t: Target, repo: string, ref: string, path: string): Promise<Blob>;
|
|
30
|
+
/** A blob answer. A run's branch on the forge answers in the same shape. */
|
|
31
|
+
export declare function file(raw: unknown, path: string): Blob;
|
package/lib/api/git.js
CHANGED
|
@@ -5,14 +5,18 @@
|
|
|
5
5
|
* GET /v1/git/repos/{name}/blob?ref=&path= one file; binary is base64,
|
|
6
6
|
* past 1 MiB it is truncated with no content
|
|
7
7
|
*
|
|
8
|
-
* A run pushes its branch to
|
|
9
|
-
*
|
|
8
|
+
* A run pushes its branch to the forge, not here, so a run's files are read
|
|
9
|
+
* through the run (changes.ts); these read a codebase as the platform holds it.
|
|
10
10
|
*/
|
|
11
11
|
import { call, query, seg } from './call.js';
|
|
12
12
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
13
13
|
export async function tree(t, repo, ref, path = '') {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
return entries(await call(t, 'GET', `/v1/git/repos/${seg(repo)}/tree${query({ ref, path })}`));
|
|
15
|
+
}
|
|
16
|
+
/** A tree answer's entries. A run's branch on the forge answers in the same shape. */
|
|
17
|
+
export function entries(raw) {
|
|
18
|
+
const o = (raw && typeof raw === 'object' ? raw : {});
|
|
19
|
+
const rows = Array.isArray(o.entries) ? o.entries : [];
|
|
16
20
|
return rows
|
|
17
21
|
.map((r) => {
|
|
18
22
|
const o = (r && typeof r === 'object' ? r : {});
|
|
@@ -26,7 +30,11 @@ export async function tree(t, repo, ref, path = '') {
|
|
|
26
30
|
.filter((e) => e.path);
|
|
27
31
|
}
|
|
28
32
|
export async function blob(t, repo, ref, path) {
|
|
29
|
-
|
|
33
|
+
return file(await call(t, 'GET', `/v1/git/repos/${seg(repo)}/blob${query({ ref, path })}`), path);
|
|
34
|
+
}
|
|
35
|
+
/** A blob answer. A run's branch on the forge answers in the same shape. */
|
|
36
|
+
export function file(raw, path) {
|
|
37
|
+
const o = (raw && typeof raw === 'object' ? raw : {});
|
|
30
38
|
const binary = o.binary === true;
|
|
31
39
|
const truncated = o.truncated === true;
|
|
32
40
|
return {
|
package/lib/api/github.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* GET /v1/provider/github/user the person's own connection
|
|
7
7
|
* POST /v1/provider/github/user/connect → {authorizeUrl}
|
|
8
8
|
* POST /v1/provider/github/user/disconnect
|
|
9
|
+
* GET /v1/provider/github/installations the accounts this org has bound the App on
|
|
9
10
|
*
|
|
10
11
|
* Paged by an opaque `after` cursor; `next` is the cursor for the page after
|
|
11
12
|
* this one, or empty on the last. Rows are read defensively: a field the
|
|
@@ -76,6 +77,18 @@ export declare function connection(t: Target): Promise<Connection>;
|
|
|
76
77
|
/** Where to send the person to authorize the platform's GitHub App — github.com only. */
|
|
77
78
|
export declare function connect(t: Target): Promise<string>;
|
|
78
79
|
export declare function disconnect(t: Target): Promise<void>;
|
|
80
|
+
/** A GitHub account the platform's App is installed on, as this org sees it. */
|
|
81
|
+
export interface Installation {
|
|
82
|
+
login: string;
|
|
83
|
+
/** `Organization` or `User`. */
|
|
84
|
+
type: string;
|
|
85
|
+
/** `all` or `selected` repositories. */
|
|
86
|
+
grant: string;
|
|
87
|
+
/** This org has bound it, and GitHub still has the App installed there. */
|
|
88
|
+
connected: boolean;
|
|
89
|
+
}
|
|
90
|
+
/** The accounts this org has bound the App on. An org admin adds one through `/v1/provider/github/connect`. */
|
|
91
|
+
export declare function installations(t: Target): Promise<Installation[]>;
|
|
79
92
|
/** One repository the GitHub connection grants this organization. */
|
|
80
93
|
export interface Grant {
|
|
81
94
|
owner: string;
|
package/lib/api/github.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* GET /v1/provider/github/user the person's own connection
|
|
7
7
|
* POST /v1/provider/github/user/connect → {authorizeUrl}
|
|
8
8
|
* POST /v1/provider/github/user/disconnect
|
|
9
|
+
* GET /v1/provider/github/installations the accounts this org has bound the App on
|
|
9
10
|
*
|
|
10
11
|
* Paged by an opaque `after` cursor; `next` is the cursor for the page after
|
|
11
12
|
* this one, or empty on the last. Rows are read defensively: a field the
|
|
@@ -71,6 +72,14 @@ export async function connect(t) {
|
|
|
71
72
|
export async function disconnect(t) {
|
|
72
73
|
await call(t, 'POST', '/v1/provider/github/user/disconnect');
|
|
73
74
|
}
|
|
75
|
+
/** The accounts this org has bound the App on. An org admin adds one through `/v1/provider/github/connect`. */
|
|
76
|
+
export async function installations(t) {
|
|
77
|
+
const raw = obj(await call(t, 'GET', '/v1/provider/github/installations'));
|
|
78
|
+
return (Array.isArray(raw.installations) ? raw.installations : [])
|
|
79
|
+
.map(obj)
|
|
80
|
+
.map((i) => ({ login: str(i.login), type: str(i.type), grant: str(i.grant), connected: i.connected === true }))
|
|
81
|
+
.filter((i) => i.login);
|
|
82
|
+
}
|
|
74
83
|
export function grant(raw) {
|
|
75
84
|
const r = obj(raw);
|
|
76
85
|
const name = str(r.name);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent's own output, read as cards.
|
|
3
|
+
*
|
|
4
|
+
* A sandbox run's agent is `dev exec`, and everything it prints reaches the
|
|
5
|
+
* run's session as `log` chunks (apps/sandbox work.go). It prints each item of
|
|
6
|
+
* its turn under a header line (hanzo dev, exec/src/event_processor_with_human_output.rs):
|
|
7
|
+
*
|
|
8
|
+
* exec / <command> in <dir> a command started
|
|
9
|
+
* succeeded in 12ms: / <output> …how it ended, then what it printed
|
|
10
|
+
* codex / <text> the agent said something
|
|
11
|
+
* apply patch / patch: completed / <paths>, then the turn's diff
|
|
12
|
+
* mcp: server/tool (completed) a tool call
|
|
13
|
+
* web search: <query>
|
|
14
|
+
* ✓ step / → step / • step its plan
|
|
15
|
+
* tokens used / <n> the end; the final message follows once more, bare
|
|
16
|
+
*
|
|
17
|
+
* The chunks are cut by a clock, not by line, so a line is read only once it
|
|
18
|
+
* is whole. Lines no header owns — reasoning, or the tail of output from before
|
|
19
|
+
* the recorded events begin — are prose. Everything here becomes TEXT on the
|
|
20
|
+
* screen; nothing is markup.
|
|
21
|
+
*/
|
|
22
|
+
import type { Card } from './turn.ts';
|
|
23
|
+
/**
|
|
24
|
+
* A shell command's words, as `sh` splits them: quotes grouped, `'"'"'`
|
|
25
|
+
* joined, a backslash escaping the next character outside single quotes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function words(line: string): string[];
|
|
28
|
+
/** The command a harness ran, out of the `bash -lc '…'` it wraps each one in. */
|
|
29
|
+
export declare function unwrap(command: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* The file a command only reads, or ''. `cat f`, `nl -ba f`, `sed -n '1,80p' f`,
|
|
32
|
+
* `head -n 40 f`, `tail f` — one file, and nothing piped, chained or redirected.
|
|
33
|
+
*/
|
|
34
|
+
export declare function reads(command: string): string;
|
|
35
|
+
/** A path to show: a relative one as it is, an absolute one by its name — never the sandbox's layout. */
|
|
36
|
+
export declare const shown: (path: string) => string;
|
|
37
|
+
/** A unified diff's sections, by the path each changes, from its first hunk on. */
|
|
38
|
+
export declare function sections(diff: string): Map<string, string>;
|
|
39
|
+
/** A reader that appends the cards it finds to `out`, keyed by `mint`. */
|
|
40
|
+
export declare function harness(out: Card[], mint: () => string): {
|
|
41
|
+
feed(chunk: string): void;
|
|
42
|
+
/** The last line even without its newline, and what only the whole output can decide. */
|
|
43
|
+
end(): void;
|
|
44
|
+
};
|