@hanzo/build 0.2.80 → 0.2.82
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 +13 -0
- package/lib/activity.d.ts +3 -7
- package/lib/activity.js +18 -5
- package/lib/api/agents.d.ts +1 -20
- package/lib/api/agents.js +16 -8
- package/lib/api/auto.d.ts +3 -23
- package/lib/api/auto.js +39 -22
- package/lib/api/billing.d.ts +0 -35
- package/lib/api/billing.js +6 -5
- package/lib/api/call.d.ts +16 -8
- package/lib/api/call.js +32 -1
- package/lib/api/capabilities.d.ts +0 -11
- package/lib/api/capabilities.js +2 -1
- package/lib/api/codebases.d.ts +0 -10
- package/lib/api/codebases.js +3 -2
- package/lib/api/coding.d.ts +3 -32
- package/lib/api/coding.js +13 -7
- package/lib/api/connectors.d.ts +1 -17
- package/lib/api/connectors.js +5 -4
- package/lib/api/environment.d.ts +6 -20
- package/lib/api/environment.js +22 -19
- package/lib/api/github.d.ts +0 -22
- package/lib/api/github.js +3 -2
- package/lib/api/keys.d.ts +0 -14
- package/lib/api/keys.js +4 -3
- package/lib/api/library.d.ts +3 -17
- package/lib/api/library.js +23 -22
- package/lib/api/links.d.ts +1 -1
- package/lib/api/links.js +1 -1
- package/lib/api/machines.d.ts +0 -15
- package/lib/api/machines.js +6 -5
- package/lib/api/mcp.d.ts +0 -9
- package/lib/api/mcp.js +3 -2
- package/lib/api/members.d.ts +0 -15
- package/lib/api/members.js +4 -3
- package/lib/api/memory.d.ts +0 -11
- package/lib/api/memory.js +2 -1
- package/lib/api/places.d.ts +1 -9
- package/lib/api/places.js +12 -1
- package/lib/api/plugins.d.ts +1 -16
- package/lib/api/plugins.js +4 -3
- package/lib/api/profile.d.ts +0 -10
- package/lib/api/profile.js +5 -4
- package/lib/api/projects.d.ts +0 -14
- package/lib/api/projects.js +11 -10
- package/lib/api/provider.d.ts +2 -14
- package/lib/api/provider.js +12 -10
- package/lib/api/sandbox.d.ts +0 -17
- package/lib/api/sandbox.js +5 -4
- package/lib/api/sessions.d.ts +0 -17
- package/lib/api/sessions.js +5 -4
- package/lib/api/skills.d.ts +1 -15
- package/lib/api/skills.js +5 -4
- package/lib/api/studio.d.ts +0 -14
- package/lib/api/studio.js +6 -5
- package/lib/api/turn.d.ts +6 -28
- package/lib/api/turn.js +105 -49
- package/lib/api/verdict.d.ts +0 -9
- package/lib/api/verdict.js +2 -1
- package/lib/api/webhooks.d.ts +1 -14
- package/lib/api/webhooks.js +5 -4
- package/lib/ask.js +7 -4
- package/lib/automations.d.ts +5 -4
- package/lib/automations.js +95 -74
- package/lib/back.js +10 -4
- package/lib/builder.js +7 -5
- package/lib/carry.js +20 -16
- package/lib/customize/agents.js +78 -31
- package/lib/customize/connectors.js +62 -48
- package/lib/customize/index.js +43 -10
- package/lib/customize/plugins.js +34 -22
- package/lib/customize/say.d.ts +13 -1
- package/lib/customize/say.js +15 -21
- package/lib/customize/skills.js +31 -22
- package/lib/customize/ui.d.ts +5 -9
- package/lib/customize/ui.js +26 -16
- package/lib/data.d.ts +1 -1
- package/lib/data.js +3 -2
- package/lib/desk.js +45 -33
- package/lib/door.js +35 -10
- package/lib/environment.js +25 -19
- package/lib/files.js +43 -34
- package/lib/find.js +12 -9
- package/lib/foot.js +7 -4
- package/lib/forge.js +61 -40
- package/lib/gear.d.ts +2 -1
- package/lib/gear.js +6 -4
- package/lib/git.js +29 -19
- package/lib/landing.d.ts +5 -5
- package/lib/landing.js +52 -44
- package/lib/library.js +73 -51
- package/lib/merge.d.ts +2 -2
- package/lib/meter.d.ts +1 -1
- package/lib/meter.js +14 -5
- package/lib/mic.js +12 -5
- package/lib/mind.d.ts +1 -0
- package/lib/mind.js +5 -3
- package/lib/nav.d.ts +6 -5
- package/lib/nav.js +37 -30
- package/lib/panel.d.ts +2 -1
- package/lib/panel.js +52 -36
- package/lib/plan.d.ts +4 -20
- package/lib/plan.js +26 -10
- package/lib/plans.js +49 -30
- package/lib/plugins/design.js +3 -2
- package/lib/plugins/index.d.ts +23 -4
- package/lib/plugins/index.js +11 -26
- package/lib/plugins/pdf.js +2 -1
- package/lib/plugins/project.js +7 -4
- package/lib/plugins/research.js +3 -2
- package/lib/plugins/sketch.js +6 -3
- package/lib/plugins/slides.js +3 -2
- package/lib/plugins/template.js +6 -3
- package/lib/plus.js +25 -13
- package/lib/prefs.d.ts +0 -14
- package/lib/prefs.js +2 -1
- package/lib/project.js +69 -62
- package/lib/prompt.d.ts +12 -11
- package/lib/prompt.js +38 -20
- package/lib/publish.js +17 -12
- package/lib/reply.js +6 -3
- package/lib/run.js +72 -54
- package/lib/say.d.ts +2 -0
- package/lib/say.js +7 -0
- package/lib/scheduled.js +54 -43
- package/lib/section.d.ts +1 -0
- package/lib/section.js +8 -2
- package/lib/settings/account.d.ts +1 -0
- package/lib/settings/account.js +31 -21
- package/lib/settings/billing.js +55 -28
- package/lib/settings/capabilities.js +26 -15
- package/lib/settings/card.js +19 -12
- package/lib/settings/code.js +22 -11
- package/lib/settings/credits.d.ts +4 -4
- package/lib/settings/credits.js +34 -21
- package/lib/settings/environments.js +21 -14
- package/lib/settings/general.js +26 -14
- package/lib/settings/index.js +6 -3
- package/lib/settings/integrations.js +42 -28
- package/lib/settings/keys.js +37 -19
- package/lib/settings/list.d.ts +0 -5
- package/lib/settings/list.js +24 -15
- package/lib/settings/machines.d.ts +3 -0
- package/lib/settings/machines.js +63 -38
- package/lib/settings/members.js +23 -14
- package/lib/settings/memory.js +12 -6
- package/lib/settings/notifications.js +32 -19
- package/lib/settings/plan.d.ts +1 -1
- package/lib/settings/plan.js +40 -17
- package/lib/settings/privacy.js +26 -13
- package/lib/settings/ui.d.ts +3 -2
- package/lib/settings/ui.js +9 -3
- package/lib/settings/usage.js +7 -3
- package/lib/shelf.js +20 -17
- package/lib/side.d.ts +1 -26
- package/lib/side.js +11 -5
- package/lib/standing.d.ts +2 -10
- package/lib/standing.js +5 -3
- package/lib/suggest.d.ts +8 -28
- package/lib/suggest.js +39 -4
- package/lib/suggestions.js +14 -5
- package/lib/summary.d.ts +1 -1
- package/lib/summary.js +4 -1
- package/lib/sync.js +22 -12
- package/lib/tools.d.ts +4 -3
- package/lib/tools.js +10 -7
- package/lib/transcript.js +10 -8
- package/package.json +4 -2
package/lib/api/coding.js
CHANGED
|
@@ -28,11 +28,17 @@
|
|
|
28
28
|
* A sandbox run keeps its sandbox after it ends, so `after` continues the earlier
|
|
29
29
|
* run in that same sandbox and working tree, resumed if it parked.
|
|
30
30
|
*/
|
|
31
|
+
import { phrase } from '@hanzo/i18n';
|
|
32
|
+
import { english } from '../say.js';
|
|
31
33
|
import { call, headers, reason, Refusal, seg } from './call.js';
|
|
32
|
-
/** Why this mode cannot run where it is sent, or '' when it can. */
|
|
33
|
-
export const unhonoured = (mode, target
|
|
34
|
-
?
|
|
35
|
-
: ''
|
|
34
|
+
/** Why this mode cannot run where it is sent, in the words of the `t` handed (English without one), or '' when it can. */
|
|
35
|
+
export const unhonoured = (mode, target, t = english) => !target?.trim()
|
|
36
|
+
? ''
|
|
37
|
+
: mode === 'plan'
|
|
38
|
+
? t('A plan runs in the Hanzo sandbox: a machine clones and pushes with its own credential. Choose Cloud, or switch to Build.')
|
|
39
|
+
: mode === 'setup'
|
|
40
|
+
? t('A setup runs in the Hanzo sandbox: a machine clones and pushes with its own credential. Choose Cloud, or switch to Build.')
|
|
41
|
+
: '';
|
|
36
42
|
/**
|
|
37
43
|
* The request body: only what was asked, with no empty strings sent as values.
|
|
38
44
|
* A run in the sandbox asks for one with a desktop, so its Desktop tab has a
|
|
@@ -53,14 +59,14 @@ export function body(ask) {
|
|
|
53
59
|
}
|
|
54
60
|
export async function start(t, ask) {
|
|
55
61
|
if (!ask.prompt.trim())
|
|
56
|
-
throw new Refusal(400, 'Say what the run should do');
|
|
62
|
+
throw new Refusal(400, phrase('Say what the run should do'));
|
|
57
63
|
const why = unhonoured(ask.mode, ask.targetId);
|
|
58
64
|
if (why)
|
|
59
65
|
throw new Refusal(400, why);
|
|
60
66
|
const r = await call(t, 'POST', '/v1/agent/coding', body(ask));
|
|
61
67
|
const s = (k) => (typeof r?.[k] === 'string' ? r[k] : '');
|
|
62
68
|
if (!s('sessionId'))
|
|
63
|
-
throw new Refusal(502, 'The run was admitted but named no session');
|
|
69
|
+
throw new Refusal(502, phrase('The run was admitted but named no session'));
|
|
64
70
|
return {
|
|
65
71
|
session: s('sessionId'),
|
|
66
72
|
repo: s('repo'),
|
|
@@ -154,7 +160,7 @@ export async function artifacts(t, session) {
|
|
|
154
160
|
export async function artifact(t, session, name) {
|
|
155
161
|
const parts = name.split('/');
|
|
156
162
|
if (parts.some((p) => p === '' || p === '.' || p === '..'))
|
|
157
|
-
throw new Refusal(400, 'That artifact has no name to fetch it by');
|
|
163
|
+
throw new Refusal(400, phrase('That artifact has no name to fetch it by'));
|
|
158
164
|
const res = await fetch(`${t.api}/v1/agent/coding/${seg(session)}/artifacts/${parts.map(seg).join('/')}`, { headers: headers(t), cache: 'no-store' });
|
|
159
165
|
if (!res.ok)
|
|
160
166
|
throw new Refusal(res.status, (await reason(res)) || `The artifact answered ${res.status}`);
|
package/lib/api/connectors.d.ts
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
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
1
|
import { type Target } from './call.ts';
|
|
18
2
|
export interface Server {
|
|
19
3
|
/** The server's id in the org, and the prefix of every tool it brings. */
|
|
@@ -79,7 +63,7 @@ export interface Adding {
|
|
|
79
63
|
header?: string;
|
|
80
64
|
secret?: string;
|
|
81
65
|
}
|
|
82
|
-
/** Why a server cannot be added as it stands, or '' when it can. */
|
|
66
|
+
/** Why a server cannot be added as it stands, or '' when it can. English, marked (`phrase`): a screen says it through `t`. */
|
|
83
67
|
export declare function refuse(a: Adding): string;
|
|
84
68
|
export declare function add(t: Target, a: Adding): Promise<Server>;
|
|
85
69
|
export declare function remove(t: Target, id: string): Promise<void>;
|
package/lib/api/connectors.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* ships only a package needs somewhere to run first. The fleet's own servers,
|
|
15
15
|
* on for every run, are mcp.ts.
|
|
16
16
|
*/
|
|
17
|
+
import { phrase } from '@hanzo/i18n';
|
|
17
18
|
import { call, query, seg } from './call.js';
|
|
18
19
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
19
20
|
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
@@ -67,16 +68,16 @@ export const owns = (s, toolName) => toolName.startsWith(`${s.id}_`);
|
|
|
67
68
|
export async function servers(t) {
|
|
68
69
|
return rows(obj(await call(t, 'GET', '/v1/tool/mcp/servers')).servers).map(server).filter((s) => s.id);
|
|
69
70
|
}
|
|
70
|
-
/** Why a server cannot be added as it stands, or '' when it can. */
|
|
71
|
+
/** Why a server cannot be added as it stands, or '' when it can. English, marked (`phrase`): a screen says it through `t`. */
|
|
71
72
|
export function refuse(a) {
|
|
72
73
|
if (!a.listing) {
|
|
73
74
|
if (!a.name?.trim())
|
|
74
|
-
return 'A connector needs a name';
|
|
75
|
+
return phrase('A connector needs a name');
|
|
75
76
|
if (!/^https?:\/\/[^\s/]+/.test(a.url?.trim() ?? ''))
|
|
76
|
-
return 'The URL is an http(s) address';
|
|
77
|
+
return phrase('The URL is an http(s) address');
|
|
77
78
|
}
|
|
78
79
|
if (a.secret && !a.header?.trim())
|
|
79
|
-
return 'Name the header the secret is sent in';
|
|
80
|
+
return phrase('Name the header the secret is sent in');
|
|
80
81
|
return '';
|
|
81
82
|
}
|
|
82
83
|
export async function add(t, a) {
|
package/lib/api/environment.d.ts
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
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
|
-
*/
|
|
1
|
+
import type { Say } from '@hanzo/i18n/react';
|
|
17
2
|
import { type Target } from './call.ts';
|
|
18
3
|
export type State = 'none' | 'proposed' | 'ready';
|
|
19
4
|
export interface Proposal {
|
|
@@ -46,9 +31,10 @@ export declare function environment(raw: unknown, repo?: string): Environment;
|
|
|
46
31
|
export declare const unset: (e: Environment) => boolean;
|
|
47
32
|
/**
|
|
48
33
|
* What a refused save or setup run says to the person who pressed it: what is
|
|
49
|
-
* wrong and what to do, in the reader's words rather than the platform's
|
|
34
|
+
* wrong and what to do, in the reader's words rather than the platform's, and
|
|
35
|
+
* in the language of the `t` handed (English without one).
|
|
50
36
|
*/
|
|
51
|
-
export declare function plain(e: unknown, repo: string, act: 'save' | 'start' | 'run'): string;
|
|
37
|
+
export declare function plain(e: unknown, repo: string, act: 'save' | 'start' | 'run', t?: Say): string;
|
|
52
38
|
export declare function environments(t: Target): Promise<Environment[]>;
|
|
53
39
|
export declare function read(t: Target, repo: string): Promise<Environment>;
|
|
54
40
|
export declare function save(t: Target, repo: string, scripts: {
|
|
@@ -58,8 +44,8 @@ export declare function save(t: Target, repo: string, scripts: {
|
|
|
58
44
|
export declare function forget(t: Target, repo: string): Promise<void>;
|
|
59
45
|
/** An environment variable name the run can export, and not one of its own. */
|
|
60
46
|
export declare const NAME: RegExp;
|
|
61
|
-
/** Why a secret name cannot be set, or '' when it can. */
|
|
62
|
-
export declare function refuse(name: string): string;
|
|
47
|
+
/** Why a secret name cannot be set, in the words of the `t` handed, or '' when it can. */
|
|
48
|
+
export declare function refuse(name: string, t?: Say): string;
|
|
63
49
|
export declare function setSecret(t: Target, repo: string, name: string, value: string): Promise<Environment>;
|
|
64
50
|
export declare function removeSecret(t: Target, repo: string, name: string): Promise<Environment>;
|
|
65
51
|
/**
|
package/lib/api/environment.js
CHANGED
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
* here as `proposal` until someone saves it. Secrets are names only; their values
|
|
15
15
|
* are sealed in KMS and reach the run's environment, never this record.
|
|
16
16
|
*/
|
|
17
|
-
import {
|
|
17
|
+
import { phrase } from '@hanzo/i18n';
|
|
18
|
+
import { english } from '../say.js';
|
|
19
|
+
import { call, Refusal, seg, told } from './call.js';
|
|
18
20
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
19
21
|
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
20
22
|
const names = (v) => (Array.isArray(v) ? v.filter((n) => typeof n === 'string' && n !== '') : []);
|
|
@@ -43,34 +45,35 @@ export function environment(raw, repo = '') {
|
|
|
43
45
|
export const unset = (e) => e.state === 'none' && !e.updated;
|
|
44
46
|
/**
|
|
45
47
|
* What a refused save or setup run says to the person who pressed it: what is
|
|
46
|
-
* wrong and what to do, in the reader's words rather than the platform's
|
|
48
|
+
* wrong and what to do, in the reader's words rather than the platform's, and
|
|
49
|
+
* in the language of the `t` handed (English without one).
|
|
47
50
|
*/
|
|
48
|
-
export function plain(e, repo, act) {
|
|
51
|
+
export function plain(e, repo, act, t = english) {
|
|
49
52
|
// This page's own refusals (a secret's name, an empty value) are already a reader's words.
|
|
50
53
|
if (e instanceof Error && !(e instanceof Refusal) && e.name === 'Error' && e.message)
|
|
51
|
-
return e
|
|
54
|
+
return told(e, t);
|
|
52
55
|
if (e instanceof Refusal) {
|
|
53
56
|
const why = e.message;
|
|
54
57
|
if (e.status === 401)
|
|
55
|
-
return 'Your session ended. Sign in again, then try again.';
|
|
58
|
+
return t('Your session ended. Sign in again, then try again.');
|
|
56
59
|
if (e.status === 403 && act === 'save')
|
|
57
|
-
return 'Only an organization admin saves an environment. Start the agent instead; an admin reviews what it proposes.';
|
|
60
|
+
return t('Only an organization admin saves an environment. Start the agent instead; an admin reviews what it proposes.');
|
|
58
61
|
if (e.status === 402)
|
|
59
|
-
return 'This run needs a plan with runs left. See Plans to change it.';
|
|
62
|
+
return t('This run needs a plan with runs left. See Plans to change it.');
|
|
60
63
|
if (e.status === 403)
|
|
61
|
-
return
|
|
64
|
+
return t('This account cannot start a run on {repo}. Ask an organization admin for access.', { repo });
|
|
62
65
|
if (e.status === 429)
|
|
63
|
-
return 'Too many runs are going right now. Try again in a minute.';
|
|
66
|
+
return t('Too many runs are going right now. Try again in a minute.');
|
|
64
67
|
if (e.status >= 500)
|
|
65
|
-
return act === 'save' ? 'The environment store is not answering right now. Try again shortly.' : 'The code workspace is not answering right now. Try again shortly.';
|
|
68
|
+
return act === 'save' ? t('The environment store is not answering right now. Try again shortly.') : t('The code workspace is not answering right now. Try again shortly.');
|
|
66
69
|
if (/no repository named|not one of this organization|is not a repository/.test(why))
|
|
67
|
-
return
|
|
70
|
+
return t('{repo} is not a repository this organization has on the forge. Choose one of its repositories.', { repo });
|
|
68
71
|
if (/no account on the forge|verify .* first|no forge identity/.test(why))
|
|
69
|
-
return 'Your forge account is not ready yet: sign in to the forge once, then try again.';
|
|
72
|
+
return t('Your forge account is not ready yet: sign in to the forge once, then try again.');
|
|
70
73
|
}
|
|
71
74
|
if (act === 'save')
|
|
72
|
-
return
|
|
73
|
-
return act === 'start' ?
|
|
75
|
+
return t('The environment for {repo} could not be saved. Try again.', { repo });
|
|
76
|
+
return act === 'start' ? t('The setup run on {repo} could not start. Try again.', { repo }) : t('The run on {repo} could not start. Try again.', { repo });
|
|
74
77
|
}
|
|
75
78
|
export async function environments(t) {
|
|
76
79
|
const r = await call(t, 'GET', '/v1/environment');
|
|
@@ -88,13 +91,13 @@ export async function forget(t, repo) {
|
|
|
88
91
|
/** An environment variable name the run can export, and not one of its own. */
|
|
89
92
|
export const NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
90
93
|
const RESERVED = ['PATH', 'HOME', 'USER', 'SHELL', 'PWD', 'HOSTNAME', 'IFS', 'ENV', 'BASH_ENV'];
|
|
91
|
-
/** Why a secret name cannot be set, or '' when it can. */
|
|
92
|
-
export function refuse(name) {
|
|
94
|
+
/** Why a secret name cannot be set, in the words of the `t` handed, or '' when it can. */
|
|
95
|
+
export function refuse(name, t = english) {
|
|
93
96
|
if (!NAME.test(name))
|
|
94
|
-
return
|
|
97
|
+
return name ? t('{name} is not an environment variable name', { name }) : t('That is not an environment variable name');
|
|
95
98
|
const up = name.toUpperCase();
|
|
96
99
|
if (up.startsWith('HANZO_') || RESERVED.includes(up))
|
|
97
|
-
return
|
|
100
|
+
return t('{name} is reserved for the run itself', { name });
|
|
98
101
|
return '';
|
|
99
102
|
}
|
|
100
103
|
export async function setSecret(t, repo, name, value) {
|
|
@@ -102,7 +105,7 @@ export async function setSecret(t, repo, name, value) {
|
|
|
102
105
|
if (why)
|
|
103
106
|
throw new Error(why);
|
|
104
107
|
if (!value)
|
|
105
|
-
throw new Error('A secret needs a value');
|
|
108
|
+
throw new Error(phrase('A secret needs a value'));
|
|
106
109
|
return environment(await call(t, 'PUT', `/v1/environment/${seg(repo)}/secrets/${seg(name)}`, { value }), repo);
|
|
107
110
|
}
|
|
108
111
|
export async function removeSecret(t, repo, name) {
|
package/lib/api/github.d.ts
CHANGED
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub, as the platform's GitHub App sees it for the signed-in person.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/provider/github/repos?q=&owner=&limit=&after= most recently pushed first
|
|
5
|
-
* GET /v1/provider/github/repos/{owner}/{repo}/branches?q=&limit=&after= default first
|
|
6
|
-
* GET /v1/provider/github/user the person's own connection
|
|
7
|
-
* POST /v1/provider/github/user/connect {return} → {authorizeUrl}
|
|
8
|
-
* POST /v1/provider/github/user/complete {grant} → the connection
|
|
9
|
-
* POST /v1/provider/github/user/disconnect
|
|
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
|
|
12
|
-
*
|
|
13
|
-
* Paged by an opaque `after` cursor; `next` is the cursor for the page after
|
|
14
|
-
* this one, or empty on the last. Rows are read defensively: a field the
|
|
15
|
-
* platform left out is an empty value here, never `undefined` in a label.
|
|
16
|
-
*
|
|
17
|
-
* `connected` false with repositories is an org admin seeing the org's
|
|
18
|
-
* installations; false with none is the cue to connect. GitHub returns a
|
|
19
|
-
* connecting person to the page the connect named as its return (a Hanzo app
|
|
20
|
-
* page; the console's /connectors otherwise) with `?complete=github&grant=<id>`,
|
|
21
|
-
* and that page completes it (`complete`, and `landed` in ../back.ts).
|
|
22
|
-
*/
|
|
23
1
|
import { type Target } from './call.ts';
|
|
24
2
|
export interface Repo {
|
|
25
3
|
owner: string;
|
package/lib/api/github.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* page; the console's /connectors otherwise) with `?complete=github&grant=<id>`,
|
|
21
21
|
* and that page completes it (`complete`, and `landed` in ../back.ts).
|
|
22
22
|
*/
|
|
23
|
+
import { phrase } from '@hanzo/i18n';
|
|
23
24
|
import { call, query, seg } from './call.js';
|
|
24
25
|
import { codebases } from './codebases.js';
|
|
25
26
|
import { links } from './links.js';
|
|
@@ -79,7 +80,7 @@ export async function connect(t, back) {
|
|
|
79
80
|
const raw = obj(await call(t, 'POST', '/v1/provider/github/user/connect', back ? { return: back } : undefined));
|
|
80
81
|
const url = str(raw.authorizeUrl);
|
|
81
82
|
if (!/^https:\/\/github\.com\//.test(url))
|
|
82
|
-
throw new Error('The platform did not name a GitHub address to connect at');
|
|
83
|
+
throw new Error(phrase('The platform did not name a GitHub address to connect at'));
|
|
83
84
|
return url;
|
|
84
85
|
}
|
|
85
86
|
/** Finish the connect GitHub's return handed this page as `grant`. */
|
|
@@ -177,7 +178,7 @@ export async function grants(t, signal) {
|
|
|
177
178
|
export async function bring(t, repos) {
|
|
178
179
|
const names = repos.map((n) => n.trim()).filter(Boolean);
|
|
179
180
|
if (names.length === 0)
|
|
180
|
-
throw new Error('Choose a repository first');
|
|
181
|
+
throw new Error(phrase('Choose a repository first'));
|
|
181
182
|
const raw = obj(await call(t, 'POST', '/v1/provider/github/repos/import', { repos: names }));
|
|
182
183
|
const said = Array.isArray(raw.repos) ? raw.repos : [];
|
|
183
184
|
const made = Array.isArray(raw.codebases) ? raw.codebases : [];
|
package/lib/api/keys.d.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
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).
|
|
5
|
-
*
|
|
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
|
|
9
|
-
*
|
|
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:*`.
|
|
14
|
-
*/
|
|
15
1
|
import { type Target } from './call.ts';
|
|
16
2
|
export declare const KINDS: readonly ['secret', 'publishable'];
|
|
17
3
|
export type Kind = (typeof KINDS)[number];
|
package/lib/api/keys.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* whole. A limit only narrows what a key may reach — `model:zen5`,
|
|
13
13
|
* `project:acme`, `product:train`, `read:*`.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { phrase } from '@hanzo/i18n';
|
|
16
|
+
import { call, Sentence } from './call.js';
|
|
16
17
|
export const KINDS = ['secret', 'publishable'];
|
|
17
18
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
18
19
|
const list = (v) => (Array.isArray(v) ? v.filter((s) => typeof s === 'string' && s !== '') : []);
|
|
@@ -49,7 +50,7 @@ export function limits(raw) {
|
|
|
49
50
|
.filter(Boolean);
|
|
50
51
|
const bad = out.find((s) => !LIMIT.test(s));
|
|
51
52
|
if (bad)
|
|
52
|
-
throw new
|
|
53
|
+
throw new Sentence(phrase('{limit} is not a limit — write it as kind:name, like model:zen5'), { limit: bad });
|
|
53
54
|
return out;
|
|
54
55
|
}
|
|
55
56
|
/** Create a new key beside the ones held. The answer is the only time a secret key is shown. */
|
|
@@ -62,7 +63,7 @@ export async function mint(t, type, name = '', limit = []) {
|
|
|
62
63
|
const r = await call(t, 'POST', '/v1/account/keys', body);
|
|
63
64
|
const key = str(r?.key);
|
|
64
65
|
if (!key)
|
|
65
|
-
throw new Error('The platform answered no key');
|
|
66
|
+
throw new Error(phrase('The platform answered no key'));
|
|
66
67
|
return { id: str(r?.id), name: str(r?.name), type: kind(r?.type) ?? type, key, limit: list(r?.limit) };
|
|
67
68
|
}
|
|
68
69
|
/** Revoke exactly one key, by id. Every other key keeps working. */
|
package/lib/api/library.d.ts
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Library: everything a person made, in Chat, Dev, Studio and workflows, in
|
|
3
|
-
* one index.
|
|
4
|
-
*
|
|
5
|
-
* GET /v1/library {data: [item], next} newest first, filtered
|
|
6
|
-
* GET /v1/library/{id} one item, with fresh signed URLs
|
|
7
|
-
* POST /v1/library keep one: its bytes as base64, at most 10 MiB
|
|
8
|
-
* PATCH /v1/library/{id} rename, share with the org, move to a project
|
|
9
|
-
* DELETE /v1/library/{id} its row and its bytes
|
|
10
|
-
*
|
|
11
|
-
* An item is its owner's; another member of the org reads it only once its owner
|
|
12
|
-
* shares it, and changes it never. Its `url`, `download` and `preview` are
|
|
13
|
-
* signed and stop working at `expires`: read the item again for fresh ones.
|
|
14
|
-
*/
|
|
15
1
|
import { type Target } from './call.ts';
|
|
16
2
|
export declare const KIND_IDS: readonly ['image', 'video', 'audio', 'document', 'slides', 'type', 'model', 'code', 'file'];
|
|
17
3
|
export type Kind = (typeof KIND_IDS)[number];
|
|
18
|
-
/** The kinds a reader narrows to, in the order the Library lists them, '' being all
|
|
4
|
+
/** The kinds a reader narrows to, in the order the Library lists them, '' being all; each label English and marked (`phrase`), drawn through `t`. */
|
|
19
5
|
export declare const KINDS: readonly {
|
|
20
6
|
id: Kind | '';
|
|
21
7
|
label: string;
|
|
@@ -23,7 +9,7 @@ export declare const KINDS: readonly {
|
|
|
23
9
|
export declare const SOURCES: readonly ['chat', 'dev', 'studio', 'workflow'];
|
|
24
10
|
export type Source = (typeof SOURCES)[number];
|
|
25
11
|
export type Scope = 'all' | 'mine' | 'shared';
|
|
26
|
-
/** A product, as a person names it. */
|
|
12
|
+
/** A product, as a person names it: Chat, Dev and Studio are names, and Workflow a word, marked (`phrase`). */
|
|
27
13
|
export declare const SOURCE: Record<Source, string>;
|
|
28
14
|
export interface Item {
|
|
29
15
|
id: string;
|
|
@@ -117,7 +103,7 @@ export declare function change(t: Target, id: string, c: Change): Promise<Item>;
|
|
|
117
103
|
export declare function remove(t: Target, id: string): Promise<void>;
|
|
118
104
|
/** A size the way a person reads it. */
|
|
119
105
|
export declare function size(bytes: number): string;
|
|
120
|
-
/** The day an instant falls on, as a section of the Library: Today, Yesterday, then the month. */
|
|
106
|
+
/** The day an instant falls on, as a section of the Library: Today, Yesterday (English, marked), then the month. */
|
|
121
107
|
export declare function day(iso: string, now?: Date): string;
|
|
122
108
|
/** Items under the day each was made, in the order given (newest first). */
|
|
123
109
|
export declare function days(list: Item[], now?: Date): {
|
package/lib/api/library.js
CHANGED
|
@@ -12,23 +12,24 @@
|
|
|
12
12
|
* shares it, and changes it never. Its `url`, `download` and `preview` are
|
|
13
13
|
* signed and stop working at `expires`: read the item again for fresh ones.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { phrase } from '@hanzo/i18n';
|
|
16
|
+
import { call, query, seg, Sentence } from './call.js';
|
|
16
17
|
export const KIND_IDS = ['image', 'video', 'audio', 'document', 'slides', 'type', 'model', 'code', 'file'];
|
|
17
|
-
/** The kinds a reader narrows to, in the order the Library lists them, '' being all
|
|
18
|
+
/** The kinds a reader narrows to, in the order the Library lists them, '' being all; each label English and marked (`phrase`), drawn through `t`. */
|
|
18
19
|
export const KINDS = [
|
|
19
|
-
{ id: '', label: 'All' },
|
|
20
|
-
{ id: 'image', label: 'Images' },
|
|
21
|
-
{ id: 'video', label: 'Videos' },
|
|
22
|
-
{ id: 'audio', label: 'Audio' },
|
|
23
|
-
{ id: 'document', label: 'Documents' },
|
|
24
|
-
{ id: 'slides', label: 'Slides' },
|
|
25
|
-
{ id: 'model', label: '3D' },
|
|
26
|
-
{ id: 'code', label: 'Code' },
|
|
27
|
-
{ id: 'file', label: 'Files' },
|
|
20
|
+
{ id: '', label: phrase('All') },
|
|
21
|
+
{ id: 'image', label: phrase('Images') },
|
|
22
|
+
{ id: 'video', label: phrase('Videos') },
|
|
23
|
+
{ id: 'audio', label: phrase('Audio') },
|
|
24
|
+
{ id: 'document', label: phrase('Documents') },
|
|
25
|
+
{ id: 'slides', label: phrase('Slides') },
|
|
26
|
+
{ id: 'model', label: phrase('3D') },
|
|
27
|
+
{ id: 'code', label: phrase('Code') },
|
|
28
|
+
{ id: 'file', label: phrase('Files') },
|
|
28
29
|
];
|
|
29
30
|
export const SOURCES = ['chat', 'dev', 'studio', 'workflow'];
|
|
30
|
-
/** A product, as a person names it. */
|
|
31
|
-
export const SOURCE = { chat: 'Chat', dev: 'Dev', studio: 'Studio', workflow: 'Workflow' };
|
|
31
|
+
/** A product, as a person names it: Chat, Dev and Studio are names, and Workflow a word, marked (`phrase`). */
|
|
32
|
+
export const SOURCE = { chat: 'Chat', dev: 'Dev', studio: 'Studio', workflow: phrase('Workflow') };
|
|
32
33
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
33
34
|
const num = (v) => (typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0);
|
|
34
35
|
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
@@ -91,7 +92,7 @@ export async function items(t, f = {}, init) {
|
|
|
91
92
|
export async function get(t, id) {
|
|
92
93
|
const x = item(await call(t, 'GET', `/v1/library/${seg(id)}`));
|
|
93
94
|
if (!x)
|
|
94
|
-
throw new Error('The item came back without an id');
|
|
95
|
+
throw new Error(phrase('The item came back without an id'));
|
|
95
96
|
return x;
|
|
96
97
|
}
|
|
97
98
|
/** The largest thing one upload carries. */
|
|
@@ -106,9 +107,9 @@ export function base64(bytes) {
|
|
|
106
107
|
/** Keep a file in the Library. */
|
|
107
108
|
export async function upload(t, file, made) {
|
|
108
109
|
if (!file.bytes.length)
|
|
109
|
-
throw new
|
|
110
|
+
throw new Sentence(phrase('{name} is empty'), { name: file.name });
|
|
110
111
|
if (file.bytes.length > INLINE)
|
|
111
|
-
throw new
|
|
112
|
+
throw new Sentence(phrase('{name} is larger than 10 MB'), { name: file.name });
|
|
112
113
|
const x = item(await call(t, 'POST', '/v1/library', {
|
|
113
114
|
name: file.name,
|
|
114
115
|
mime: file.type || 'application/octet-stream',
|
|
@@ -116,13 +117,13 @@ export async function upload(t, file, made) {
|
|
|
116
117
|
...made,
|
|
117
118
|
}));
|
|
118
119
|
if (!x)
|
|
119
|
-
throw new Error('The item was kept and came back without an id');
|
|
120
|
+
throw new Error(phrase('The item was kept and came back without an id'));
|
|
120
121
|
return x;
|
|
121
122
|
}
|
|
122
123
|
export async function change(t, id, c) {
|
|
123
124
|
const x = item(await call(t, 'PATCH', `/v1/library/${seg(id)}`, c));
|
|
124
125
|
if (!x)
|
|
125
|
-
throw new Error('The item was changed and came back without an id');
|
|
126
|
+
throw new Error(phrase('The item was changed and came back without an id'));
|
|
126
127
|
return x;
|
|
127
128
|
}
|
|
128
129
|
export async function remove(t, id) {
|
|
@@ -141,17 +142,17 @@ export function size(bytes) {
|
|
|
141
142
|
}
|
|
142
143
|
return `${n < 10 ? n.toFixed(1) : Math.round(n)} ${units[u]}`;
|
|
143
144
|
}
|
|
144
|
-
/** The day an instant falls on, as a section of the Library: Today, Yesterday, then the month. */
|
|
145
|
+
/** The day an instant falls on, as a section of the Library: Today, Yesterday (English, marked), then the month. */
|
|
145
146
|
export function day(iso, now = new Date()) {
|
|
146
147
|
const d = new Date(iso);
|
|
147
148
|
if (Number.isNaN(d.getTime()))
|
|
148
|
-
return 'Earlier';
|
|
149
|
+
return phrase('Earlier');
|
|
149
150
|
const start = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
|
|
150
151
|
const days = Math.round((start(now) - start(d)) / 86_400_000);
|
|
151
152
|
if (days <= 0)
|
|
152
|
-
return 'Today';
|
|
153
|
+
return phrase('Today');
|
|
153
154
|
if (days === 1)
|
|
154
|
-
return 'Yesterday';
|
|
155
|
+
return phrase('Yesterday');
|
|
155
156
|
return d.toLocaleDateString(undefined, d.getFullYear() === now.getFullYear() ? { month: 'long' } : { month: 'long', year: 'numeric' });
|
|
156
157
|
}
|
|
157
158
|
/** Items under the day each was made, in the order given (newest first). */
|
package/lib/api/links.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `conflict`, `pending` (not on the forge yet) or `paused` — and is absent when
|
|
13
13
|
* the forge could not be read, which is not reported as healthy.
|
|
14
14
|
*
|
|
15
|
-
* Every link the sync app holds is also a row of GET /v1/
|
|
15
|
+
* Every link the sync app holds is also a row of GET /v1/project (apps/sync
|
|
16
16
|
* project.go), so this list is the org's linked projects and not a copy of them.
|
|
17
17
|
*/
|
|
18
18
|
import { type Target } from './call.ts';
|
package/lib/api/links.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `conflict`, `pending` (not on the forge yet) or `paused` — and is absent when
|
|
13
13
|
* the forge could not be read, which is not reported as healthy.
|
|
14
14
|
*
|
|
15
|
-
* Every link the sync app holds is also a row of GET /v1/
|
|
15
|
+
* Every link the sync app holds is also a row of GET /v1/project (apps/sync
|
|
16
16
|
* project.go), so this list is the org's linked projects and not a copy of them.
|
|
17
17
|
*/
|
|
18
18
|
import { call } from './call.js';
|
package/lib/api/machines.d.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The org's machines: its own computers a run can be sent to, beside the sandbox.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/agent/targets {targets: [...]}, newest first
|
|
5
|
-
* POST /v1/agent/targets register one by hand {label, kind, host}
|
|
6
|
-
* PATCH /v1/agent/targets/{id} rename it, drain it, bring it back
|
|
7
|
-
* DELETE /v1/agent/targets/{id} deregister it
|
|
8
|
-
* POST /v1/agent/targets/{id}/key mint or rotate its claim key, answered once
|
|
9
|
-
*
|
|
10
|
-
* A machine belongs to the member who registered it. Only that member or an org
|
|
11
|
-
* admin changes, removes or keys it, and the platform answers anyone else the
|
|
12
|
-
* same 404 an unknown id gets — the record names no owner — so a 404 here is
|
|
13
|
-
* said as that rule. `hanzo link`, run on the machine, registers it under its
|
|
14
|
-
* hostname and keeps it online while it runs.
|
|
15
|
-
*/
|
|
16
1
|
import { type Target } from './call.ts';
|
|
17
2
|
export declare const KINDS: readonly ['laptop', 'gpu', 'cloud', 'cluster', 'machine'];
|
|
18
3
|
export type Kind = (typeof KINDS)[number];
|
package/lib/api/machines.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* said as that rule. `hanzo link`, run on the machine, registers it under its
|
|
14
14
|
* hostname and keeps it online while it runs.
|
|
15
15
|
*/
|
|
16
|
+
import { phrase } from '@hanzo/i18n';
|
|
16
17
|
import { call, Refusal, seg } from './call.js';
|
|
17
18
|
export const KINDS = ['laptop', 'gpu', 'cloud', 'cluster', 'machine'];
|
|
18
19
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
@@ -142,7 +143,7 @@ async function owned(p) {
|
|
|
142
143
|
}
|
|
143
144
|
catch (e) {
|
|
144
145
|
if (e instanceof Refusal && e.status === 404)
|
|
145
|
-
throw new Refusal(404, 'This machine is gone, or not yours to change: only the member who linked it, or an org admin, can.');
|
|
146
|
+
throw new Refusal(404, phrase('This machine is gone, or not yours to change: only the member who linked it, or an org admin, can.'));
|
|
146
147
|
throw e;
|
|
147
148
|
}
|
|
148
149
|
}
|
|
@@ -154,7 +155,7 @@ export async function add(t, what) {
|
|
|
154
155
|
const host = (what.host ?? '').trim();
|
|
155
156
|
const label = what.label.trim() || host;
|
|
156
157
|
if (!label)
|
|
157
|
-
throw new Error('A machine needs a name or a hostname');
|
|
158
|
+
throw new Error(phrase('A machine needs a name or a hostname'));
|
|
158
159
|
const body = { label, kind: what.kind };
|
|
159
160
|
if (host)
|
|
160
161
|
body.host = host;
|
|
@@ -168,7 +169,7 @@ export async function add(t, what) {
|
|
|
168
169
|
* once one has; before that it is `not seen yet`, and nothing is there to take a run.
|
|
169
170
|
*/
|
|
170
171
|
export function state(m) {
|
|
171
|
-
return m.status === 'online' && !m.seen ? 'not seen yet' : m.status;
|
|
172
|
+
return m.status === 'online' && !m.seen ? phrase('not seen yet') : m.status;
|
|
172
173
|
}
|
|
173
174
|
/** Rename a machine, or drain it (`draining`) and bring it back (`online`). */
|
|
174
175
|
export async function change(t, id, what) {
|
|
@@ -176,7 +177,7 @@ export async function change(t, id, what) {
|
|
|
176
177
|
if (what.label !== undefined) {
|
|
177
178
|
const label = what.label.trim();
|
|
178
179
|
if (!label)
|
|
179
|
-
throw new Error('A machine needs a name');
|
|
180
|
+
throw new Error(phrase('A machine needs a name'));
|
|
180
181
|
body.label = label;
|
|
181
182
|
}
|
|
182
183
|
if (what.status !== undefined)
|
|
@@ -201,6 +202,6 @@ export async function key(t, id) {
|
|
|
201
202
|
const r = await owned(call(t, 'POST', `/v1/agent/targets/${seg(id)}/key`));
|
|
202
203
|
const k = str(r?.claimKey);
|
|
203
204
|
if (!k)
|
|
204
|
-
throw new Error('The platform answered no key');
|
|
205
|
+
throw new Error(phrase('The platform answered no key'));
|
|
205
206
|
return k;
|
|
206
207
|
}
|
package/lib/api/mcp.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The fleet's native MCP servers, as the MCP server itself lists them.
|
|
3
|
-
*
|
|
4
|
-
* POST /v1/mcp {"method":"tools/list"} → {result:{tools:[…]}}
|
|
5
|
-
*
|
|
6
|
-
* One tool is one native server. Its operations are the names in `op`. A run
|
|
7
|
-
* starts a server the first time it calls it; the server stays up afterwards.
|
|
8
|
-
* `describe` is how a caller reads one operation's schema, so it is not a server.
|
|
9
|
-
*/
|
|
10
1
|
import { type Target } from './call.ts';
|
|
11
2
|
export interface Native {
|
|
12
3
|
name: string;
|
package/lib/api/mcp.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* starts a server the first time it calls it; the server stays up afterwards.
|
|
8
8
|
* `describe` is how a caller reads one operation's schema, so it is not a server.
|
|
9
9
|
*/
|
|
10
|
+
import { phrase } from '@hanzo/i18n';
|
|
10
11
|
import { call, Refusal } from './call.js';
|
|
11
12
|
const words = (v) => (Array.isArray(v) ? v.filter((x) => typeof x === 'string' && x !== '') : []);
|
|
12
13
|
/** The servers in one tools/list answer. A missing tool array is a refusal. */
|
|
@@ -15,12 +16,12 @@ export function nativesOf(body) {
|
|
|
15
16
|
const err = root.error;
|
|
16
17
|
if (err && typeof err === 'object') {
|
|
17
18
|
const message = err.message;
|
|
18
|
-
throw new Refusal(200, typeof message === 'string' && message ? message : 'The MCP server refused the list');
|
|
19
|
+
throw new Refusal(200, typeof message === 'string' && message ? message : phrase('The MCP server refused the list'));
|
|
19
20
|
}
|
|
20
21
|
const result = root.result && typeof root.result === 'object' ? root.result : null;
|
|
21
22
|
const tools = result && Array.isArray(result.tools) ? result.tools : null;
|
|
22
23
|
if (!tools)
|
|
23
|
-
throw new Refusal(502, 'The MCP server did not list its tools');
|
|
24
|
+
throw new Refusal(502, phrase('The MCP server did not list its tools'));
|
|
24
25
|
const out = [];
|
|
25
26
|
for (const raw of tools) {
|
|
26
27
|
if (!raw || typeof raw !== 'object')
|
package/lib/api/members.d.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Who is in the organization, and who has been asked to join it, as Hanzo IAM
|
|
3
|
-
* holds them.
|
|
4
|
-
*
|
|
5
|
-
* GET /v1/iam/memberships?org= the roster: {status, data: [membership], data2: count}
|
|
6
|
-
* GET /v1/iam/invitations?owner= {invitations, total} (org admin)
|
|
7
|
-
* POST /v1/iam/invitations issue one (org admin)
|
|
8
|
-
* DELETE /v1/iam/invitations/{owner}/{name} withdraw one (org admin)
|
|
9
|
-
* GET /v1/iam/organizations/admin/{org} the org's record, whose `logo` is its mark (member)
|
|
10
|
-
*
|
|
11
|
-
* IAM decides the scope from the credential: a member reads the roster of an org
|
|
12
|
-
* they belong to, and only an admin of the org reads or writes its invitations.
|
|
13
|
-
* An invitation is a code IAM redeems when someone signs up with it; IAM sends
|
|
14
|
-
* no email of its own.
|
|
15
|
-
*/
|
|
16
1
|
import { type Target } from './call.ts';
|
|
17
2
|
export type Role = 'owner' | 'admin' | 'member';
|
|
18
3
|
export interface Member {
|