@hanzo/build 0.2.79 → 0.2.81
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 +22 -4
- package/lib/activity.d.ts +3 -7
- package/lib/activity.js +18 -5
- package/lib/api/agents.d.ts +36 -35
- package/lib/api/agents.js +66 -29
- 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/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 +2 -1
- 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.d.ts +3 -0
- package/lib/customize/agents.js +152 -55
- package/lib/customize/connectors.js +62 -48
- package/lib/customize/index.js +45 -14
- 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/host.d.ts +3 -1
- package/lib/landing.d.ts +5 -5
- package/lib/landing.js +52 -44
- package/lib/library.js +73 -51
- 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 +6 -3
- 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 +18 -15
- 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/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/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 {
|
package/lib/api/members.js
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* An invitation is a code IAM redeems when someone signs up with it; IAM sends
|
|
14
14
|
* no email of its own.
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
16
|
+
import { phrase } from '@hanzo/i18n';
|
|
17
|
+
import { call, query, seg, Sentence } from './call.js';
|
|
17
18
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
18
19
|
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
19
20
|
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
@@ -34,7 +35,7 @@ export function member(raw) {
|
|
|
34
35
|
export async function roster(t, org) {
|
|
35
36
|
const raw = obj(await call(t, 'GET', `/v1/iam/memberships${query({ org })}`));
|
|
36
37
|
if (raw.status === 'error')
|
|
37
|
-
throw new Error(str(raw.msg) || 'IAM refused the roster');
|
|
38
|
+
throw new Error(str(raw.msg) || phrase('IAM refused the roster'));
|
|
38
39
|
const rank = (r) => ROLES.indexOf(r);
|
|
39
40
|
return arr(raw.data)
|
|
40
41
|
.map(obj)
|
|
@@ -71,7 +72,7 @@ function hex(bytes) {
|
|
|
71
72
|
export async function invite(t, org, email) {
|
|
72
73
|
const address = email.trim().toLowerCase();
|
|
73
74
|
if (!EMAIL.test(address))
|
|
74
|
-
throw new
|
|
75
|
+
throw email.trim() ? new Sentence(phrase('{email} is not an email address'), { email: email.trim() }) : new Error(phrase('That is not an email address'));
|
|
75
76
|
const local = address.slice(0, address.indexOf('@')).replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24);
|
|
76
77
|
return invitation(await call(t, 'POST', '/v1/iam/invitations', {
|
|
77
78
|
owner: org,
|
package/lib/api/memory.d.ts
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* What Hanzo remembers about the signed-in person, in the org they act in.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/ai/memory/list?limit= {status, data: [memory]}, newest first
|
|
5
|
-
* POST /v1/ai/memory/remember {content} → {status, data: memory}
|
|
6
|
-
* POST /v1/ai/memory/delete {id: "<owner>/<name>"}
|
|
7
|
-
*
|
|
8
|
-
* The platform keys every memory on the validated caller and org, never on the
|
|
9
|
-
* request, so these read and change the caller's own and nobody else's. A
|
|
10
|
-
* refusal arrives in the envelope, usually under a 200.
|
|
11
|
-
*/
|
|
12
1
|
import { type Target } from './call.ts';
|
|
13
2
|
export interface Memory {
|
|
14
3
|
/** `<owner>/<name>`, what a delete names. */
|
package/lib/api/memory.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* request, so these read and change the caller's own and nobody else's. A
|
|
10
10
|
* refusal arrives in the envelope, usually under a 200.
|
|
11
11
|
*/
|
|
12
|
+
import { phrase } from '@hanzo/i18n';
|
|
12
13
|
import { call, query, unwrap } from './call.js';
|
|
13
14
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
14
15
|
export function memoryOf(raw) {
|
|
@@ -26,7 +27,7 @@ export async function memories(t, limit = 100) {
|
|
|
26
27
|
export async function remember(t, content) {
|
|
27
28
|
const said = content.trim();
|
|
28
29
|
if (!said)
|
|
29
|
-
throw new Error('Say what Hanzo should remember');
|
|
30
|
+
throw new Error(phrase('Say what Hanzo should remember'));
|
|
30
31
|
return memoryOf(unwrap(await call(t, 'POST', '/v1/ai/memory/remember', { content: said })));
|
|
31
32
|
}
|
|
32
33
|
export async function forget(t, id) {
|
package/lib/api/places.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where a run can run: the platform's sandbox, or one of the org's machines.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/agent/targets → {targets: [...]}, newest first (machines.ts)
|
|
5
|
-
*
|
|
6
|
-
* The sandbox is not a row the platform lists — it is what a run gets when it
|
|
7
|
-
* names no target — so it is the first place here and carries no id. A machine
|
|
8
|
-
* can take a run only while it is online.
|
|
9
|
-
*/
|
|
10
1
|
import type { Target } from './call.ts';
|
|
11
2
|
export interface Place {
|
|
12
3
|
/** `tgt_…`, or '' for the sandbox. */
|
|
@@ -19,6 +10,7 @@ export interface Place {
|
|
|
19
10
|
/** "10 vCPU / 122G / 1× GB10", when the machine reported one. */
|
|
20
11
|
capacity: string;
|
|
21
12
|
}
|
|
13
|
+
/** The sandbox, named in English and marked (`phrase`): a screen draws its label through `t`. */
|
|
22
14
|
export declare const SANDBOX: Place;
|
|
23
15
|
export declare function places(t: Target): Promise<Place[]>;
|
|
24
16
|
/** Whether a run sent here now would be taken. */
|
package/lib/api/places.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a run can run: the platform's sandbox, or one of the org's machines.
|
|
3
|
+
*
|
|
4
|
+
* GET /v1/agent/targets → {targets: [...]}, newest first (machines.ts)
|
|
5
|
+
*
|
|
6
|
+
* The sandbox is not a row the platform lists — it is what a run gets when it
|
|
7
|
+
* names no target — so it is the first place here and carries no id. A machine
|
|
8
|
+
* can take a run only while it is online.
|
|
9
|
+
*/
|
|
10
|
+
import { phrase } from '@hanzo/i18n';
|
|
1
11
|
import { machines, state } from './machines.js';
|
|
2
|
-
|
|
12
|
+
/** The sandbox, named in English and marked (`phrase`): a screen draws its label through `t`. */
|
|
13
|
+
export const SANDBOX = { id: '', label: phrase('Hanzo sandbox'), kind: 'sandbox', status: 'online', capacity: '' };
|
|
3
14
|
export async function places(t) {
|
|
4
15
|
const list = await machines(t);
|
|
5
16
|
return [SANDBOX, ...list.map((m) => ({ id: m.id, label: m.label, kind: m.kind, status: state(m), capacity: m.capacity }))];
|
package/lib/api/plugins.d.ts
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Plugins: TypeScript connectors an org builds, and what this deployment mounts.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/tool/plugins/authored {plugins: [plugin]}, newest first, each with its source
|
|
5
|
-
* POST /v1/tool/plugins/build {name, provider?, source | spec} → 201 {bytes, generated, plugin}
|
|
6
|
-
* DELETE /v1/tool/plugins/authored/{id} {deleted}
|
|
7
|
-
* GET /v1/tool/plugins {plugins: [{name, enabled, prefixes}]}
|
|
8
|
-
*
|
|
9
|
-
* Building is the gate: the platform bundles the source and compiles it in the
|
|
10
|
-
* runtime that will run it, and keeps it only if both succeed — a failure is a
|
|
11
|
-
* 422 whose reason is the bundler's. A spec (an OpenAPI document or prose) has a
|
|
12
|
-
* model write the source, which comes back to be read. A plugin never carries a
|
|
13
|
-
* credential: it names the connectors provider whose credential it reads when it
|
|
14
|
-
* runs. The mounted list is the deployment's own subsystems, read-only.
|
|
15
|
-
*/
|
|
16
1
|
import { type Target } from './call.ts';
|
|
17
2
|
export interface Plugin {
|
|
18
3
|
/** What a delete addresses. */
|
|
@@ -49,7 +34,7 @@ export interface Build {
|
|
|
49
34
|
source: string;
|
|
50
35
|
spec: string;
|
|
51
36
|
}
|
|
52
|
-
/** Why a plugin cannot be built as it stands, or '' when it can. */
|
|
37
|
+
/** Why a plugin cannot be built as it stands, or '' when it can. English, marked (`phrase`): a screen says it through `t`. */
|
|
53
38
|
export declare function refuse(b: Build): string;
|
|
54
39
|
export declare function build(t: Target, b: Build): Promise<Built>;
|
|
55
40
|
export declare function remove(t: Target, id: string): Promise<void>;
|
package/lib/api/plugins.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* credential: it names the connectors provider whose credential it reads when it
|
|
14
14
|
* runs. The mounted list is the deployment's own subsystems, read-only.
|
|
15
15
|
*/
|
|
16
|
+
import { phrase } from '@hanzo/i18n';
|
|
16
17
|
import { call, seg } from './call.js';
|
|
17
18
|
/** A plugin's name: one lowercase path segment, the id it runs by. */
|
|
18
19
|
export const NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
@@ -35,12 +36,12 @@ export async function mounted(t) {
|
|
|
35
36
|
})
|
|
36
37
|
.filter((m) => m.name);
|
|
37
38
|
}
|
|
38
|
-
/** Why a plugin cannot be built as it stands, or '' when it can. */
|
|
39
|
+
/** Why a plugin cannot be built as it stands, or '' when it can. English, marked (`phrase`): a screen says it through `t`. */
|
|
39
40
|
export function refuse(b) {
|
|
40
41
|
if (!NAME.test(b.name))
|
|
41
|
-
return 'A name is one lowercase word: letters, digits, _ or -';
|
|
42
|
+
return phrase('A name is one lowercase word: letters, digits, _ or -');
|
|
42
43
|
if (!b.source.trim() === !b.spec.trim())
|
|
43
|
-
return 'Give the TypeScript or the API it calls, one of the two';
|
|
44
|
+
return phrase('Give the TypeScript or the API it calls, one of the two');
|
|
44
45
|
return '';
|
|
45
46
|
}
|
|
46
47
|
export async function build(t, b) {
|
package/lib/api/profile.d.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The signed-in person's own profile on Hanzo IAM: the name they are shown by
|
|
3
|
-
* and their photo.
|
|
4
|
-
*
|
|
5
|
-
* PUT /v1/iam/account {displayName} → {status, data: {owner, name, displayName, avatar, …}}
|
|
6
|
-
* POST /v1/account/avatar multipart `file` → {avatar} the photo, also written to IAM
|
|
7
|
-
*
|
|
8
|
-
* Both act on the caller alone: neither request names a person. The email is a
|
|
9
|
-
* sign-in identifier and changes only through IAM's verification, never here.
|
|
10
|
-
*/
|
|
11
1
|
import { type Target } from './call.ts';
|
|
12
2
|
export interface Profile {
|
|
13
3
|
displayName: string;
|
package/lib/api/profile.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* Both act on the caller alone: neither request names a person. The email is a
|
|
9
9
|
* sign-in identifier and changes only through IAM's verification, never here.
|
|
10
10
|
*/
|
|
11
|
+
import { phrase } from '@hanzo/i18n';
|
|
11
12
|
import { call, headers, reason, Refusal, unwrap } from './call.js';
|
|
12
13
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
13
14
|
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
@@ -15,7 +16,7 @@ const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
|
15
16
|
export async function rename(t, name) {
|
|
16
17
|
const displayName = name.trim();
|
|
17
18
|
if (!displayName)
|
|
18
|
-
throw new Error('A name cannot be empty');
|
|
19
|
+
throw new Error(phrase('A name cannot be empty'));
|
|
19
20
|
const p = obj(unwrap(await call(t, 'PUT', '/v1/iam/account', { displayName })));
|
|
20
21
|
return { displayName: str(p.displayName), avatar: str(p.avatar) };
|
|
21
22
|
}
|
|
@@ -25,9 +26,9 @@ const LIMIT = 8 << 20;
|
|
|
25
26
|
/** Store a new photo and answer its address. */
|
|
26
27
|
export async function photo(t, file) {
|
|
27
28
|
if (!file.size)
|
|
28
|
-
throw new Error('That file is empty');
|
|
29
|
+
throw new Error(phrase('That file is empty'));
|
|
29
30
|
if (file.size > LIMIT)
|
|
30
|
-
throw new Error('A photo is at most 8 MB');
|
|
31
|
+
throw new Error(phrase('A photo is at most 8 MB'));
|
|
31
32
|
const form = new FormData();
|
|
32
33
|
form.append('file', file);
|
|
33
34
|
const res = await fetch(`${t.api}/v1/account/avatar`, { method: 'POST', headers: headers(t), body: form, cache: 'no-store' });
|
|
@@ -35,6 +36,6 @@ export async function photo(t, file) {
|
|
|
35
36
|
throw new Refusal(res.status, (await reason(res)) || `POST /v1/account/avatar answered ${res.status}`);
|
|
36
37
|
const url = str(obj(await res.json()).avatar);
|
|
37
38
|
if (!url.startsWith('https://'))
|
|
38
|
-
throw new Refusal(502, 'The photo was stored without an address this page can show');
|
|
39
|
+
throw new Refusal(502, phrase('The photo was stored without an address this page can show'));
|
|
39
40
|
return url;
|
|
40
41
|
}
|
package/lib/api/projects.d.ts
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The org's projects: what it has built, and where each one is served.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/projects → Project[]
|
|
5
|
-
* PATCH /v1/projects/{slug} rename it, or make it public or private
|
|
6
|
-
* DELETE /v1/projects/{slug} delete it and take its site down
|
|
7
|
-
* GET /v1/projects/{slug}/deployments → Deployment[], newest first
|
|
8
|
-
*
|
|
9
|
-
* `slug` is the one key every surface shares — the address under /dev, the
|
|
10
|
-
* site's name, the `project` a run is tagged with. A project belongs to the org,
|
|
11
|
-
* not to a person: the record names no author, so any member lists, changes and
|
|
12
|
-
* deletes it. Private is paid, and an unfunded org is refused rather than left
|
|
13
|
-
* public without being told.
|
|
14
|
-
*/
|
|
15
1
|
import { type Target } from './call.ts';
|
|
16
2
|
export interface Project {
|
|
17
3
|
slug: string;
|
package/lib/api/projects.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* deletes it. Private is paid, and an unfunded org is refused rather than left
|
|
13
13
|
* public without being told.
|
|
14
14
|
*/
|
|
15
|
+
import { phrase } from '@hanzo/i18n';
|
|
15
16
|
import { call, seg } from './call.js';
|
|
16
17
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
17
18
|
const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
|
|
@@ -64,7 +65,7 @@ export async function change(t, slug, what) {
|
|
|
64
65
|
if (what.name !== undefined) {
|
|
65
66
|
const n = what.name.trim();
|
|
66
67
|
if (!n)
|
|
67
|
-
throw new Error('A project needs a name');
|
|
68
|
+
throw new Error(phrase('A project needs a name'));
|
|
68
69
|
body.name = n;
|
|
69
70
|
}
|
|
70
71
|
if (what.visibility !== undefined)
|
package/lib/api/provider.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The organization's connectors: a Slack workspace, the GitHub accounts the
|
|
3
|
-
* platform's GitHub App is installed on, and the social accounts automations
|
|
4
|
-
* post to.
|
|
5
|
-
*
|
|
6
|
-
* GET /v1/provider/{id} one connector and this org's connection to it
|
|
7
|
-
* POST /v1/provider/{id}/connect {return} → {authorizeUrl}: the provider's own consent page (org admin)
|
|
8
|
-
* POST /v1/provider/{id}/disconnect forget the org's connection (org admin)
|
|
9
|
-
* GET /v1/provider/slack/channels the connected workspace's channels, a page at a time
|
|
10
|
-
*
|
|
11
|
-
* Connecting leaves this page once, for the provider's consent screen, and only
|
|
12
|
-
* for that provider's own host.
|
|
13
|
-
*/
|
|
14
1
|
import { type Target } from './call.ts';
|
|
15
2
|
export interface Connector {
|
|
16
3
|
id: string;
|
|
@@ -30,7 +17,8 @@ export declare function read(t: Target, id: string): Promise<Connector>;
|
|
|
30
17
|
* The social accounts an organization connects. An automation posts text to
|
|
31
18
|
* the ones that take it; Instagram and TikTok take only images and video, and
|
|
32
19
|
* say so. `needs` is what connecting waits on while Hanzo's app with the
|
|
33
|
-
* platform is not set up.
|
|
20
|
+
* platform is not set up. Its sentences are English and marked (`phrase`):
|
|
21
|
+
* a screen draws them through `t`.
|
|
34
22
|
*/
|
|
35
23
|
export declare const SOCIAL: readonly {
|
|
36
24
|
id: string;
|
package/lib/api/provider.js
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* Connecting leaves this page once, for the provider's consent screen, and only
|
|
12
12
|
* for that provider's own host.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { phrase } from '@hanzo/i18n';
|
|
15
|
+
import { call, query, seg, Sentence } from './call.js';
|
|
15
16
|
const str = (v) => (typeof v === 'string' ? v : '');
|
|
16
17
|
const obj = (v) => (v && typeof v === 'object' ? v : {});
|
|
17
18
|
const arr = (v) => (Array.isArray(v) ? v : []);
|
|
@@ -45,25 +46,26 @@ const CONSENT = {
|
|
|
45
46
|
* The social accounts an organization connects. An automation posts text to
|
|
46
47
|
* the ones that take it; Instagram and TikTok take only images and video, and
|
|
47
48
|
* say so. `needs` is what connecting waits on while Hanzo's app with the
|
|
48
|
-
* platform is not set up.
|
|
49
|
+
* platform is not set up. Its sentences are English and marked (`phrase`):
|
|
50
|
+
* a screen draws them through `t`.
|
|
49
51
|
*/
|
|
50
52
|
export const SOCIAL = [
|
|
51
|
-
{ id: 'x', name: 'X', posts: true, needs: 'X posting needs Hanzo’s X developer app with paid API access; it is not set up yet.' },
|
|
52
|
-
{ id: 'linkedin', name: 'LinkedIn', posts: true, needs: 'LinkedIn posting needs Hanzo’s LinkedIn app with Share on LinkedIn; it is not set up yet.' },
|
|
53
|
-
{ id: 'facebook', name: 'Facebook', posts: true, needs: 'Facebook Page posting needs Hanzo’s Meta app, approved by Meta App Review to publish to Pages; it is not set up yet.' },
|
|
53
|
+
{ id: 'x', name: 'X', posts: true, needs: phrase('X posting needs Hanzo’s X developer app with paid API access; it is not set up yet.') },
|
|
54
|
+
{ id: 'linkedin', name: 'LinkedIn', posts: true, needs: phrase('LinkedIn posting needs Hanzo’s LinkedIn app with Share on LinkedIn; it is not set up yet.') },
|
|
55
|
+
{ id: 'facebook', name: 'Facebook', posts: true, needs: phrase('Facebook Page posting needs Hanzo’s Meta app, approved by Meta App Review to publish to Pages; it is not set up yet.') },
|
|
54
56
|
{
|
|
55
57
|
id: 'instagram',
|
|
56
58
|
name: 'Instagram',
|
|
57
59
|
posts: false,
|
|
58
|
-
needs: 'Instagram posting needs Hanzo’s Meta app, approved by Meta App Review; it is not set up yet.',
|
|
59
|
-
takes: 'Instagram takes photos and reels, not text, so automations do not post to it.',
|
|
60
|
+
needs: phrase('Instagram posting needs Hanzo’s Meta app, approved by Meta App Review; it is not set up yet.'),
|
|
61
|
+
takes: phrase('Instagram takes photos and reels, not text, so automations do not post to it.'),
|
|
60
62
|
},
|
|
61
63
|
{
|
|
62
64
|
id: 'tiktok',
|
|
63
65
|
name: 'TikTok',
|
|
64
66
|
posts: false,
|
|
65
|
-
needs: 'TikTok posting needs TikTok’s audit of Hanzo’s app; it is not set up yet.',
|
|
66
|
-
takes: 'TikTok takes videos and photos, not text, so automations do not post to it.',
|
|
67
|
+
needs: phrase('TikTok posting needs TikTok’s audit of Hanzo’s app; it is not set up yet.'),
|
|
68
|
+
takes: phrase('TikTok takes videos and photos, not text, so automations do not post to it.'),
|
|
67
69
|
},
|
|
68
70
|
];
|
|
69
71
|
/** The provider's consent page for this org; its return lands on `back` when that is a Hanzo app page. */
|
|
@@ -71,7 +73,7 @@ export async function authorize(t, id, back) {
|
|
|
71
73
|
const url = str(obj(await call(t, 'POST', `/v1/provider/${seg(id)}/connect`, back ? { return: back } : {})).authorizeUrl);
|
|
72
74
|
const host = CONSENT[id];
|
|
73
75
|
if (!host || !host.test(url))
|
|
74
|
-
throw new
|
|
76
|
+
throw new Sentence(phrase('The platform did not name a {provider} address to connect at'), { provider: id });
|
|
75
77
|
return url;
|
|
76
78
|
}
|
|
77
79
|
export async function disconnect(t, id) {
|
package/lib/api/sandbox.d.ts
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A run's sandbox. It outlives the run: kept after the run ends, parked (its pod
|
|
3
|
-
* stopped, its disk and working tree kept) while nobody watches, and retired a
|
|
4
|
-
* day after it parks; a free plan's sandbox is not parked, and ends within the
|
|
5
|
-
* hour. Resume gives a parked one a pod again under the same id and tree.
|
|
6
|
-
*
|
|
7
|
-
* GET /v1/sandbox/{id} its status and when its lease ends
|
|
8
|
-
* POST /v1/sandbox/{id}/pause park it: stop the pod, keep the disk
|
|
9
|
-
* POST /v1/sandbox/{id}/resume a pod again, same id and /work
|
|
10
|
-
* POST /v1/sandbox/{id}/{screen|terminal}/ticket one ticket, spent when the page's socket opens (30 s)
|
|
11
|
-
* GET /v1/sandbox/{id}/ports what listens in it, each with its preview host
|
|
12
|
-
* POST /v1/sandbox/{id}/preview {port} an address that opens that port in a browser
|
|
13
|
-
* POST /v1/sandbox/read {id, path} a directory's names, or a file's bytes
|
|
14
|
-
*
|
|
15
|
-
* A door or a preview asked of a sandbox that stopped as it was asked is 409: it
|
|
16
|
-
* is resumed, and asked again once.
|
|
17
|
-
*/
|
|
18
1
|
import { type Target } from './call.ts';
|
|
19
2
|
export type Door = 'screen' | 'terminal';
|
|
20
3
|
/** Where a sandbox is in its life. */
|
package/lib/api/sandbox.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* A door or a preview asked of a sandbox that stopped as it was asked is 409: it
|
|
16
16
|
* is resumed, and asked again once.
|
|
17
17
|
*/
|
|
18
|
+
import { phrase } from '@hanzo/i18n';
|
|
18
19
|
import { call, Refusal, seg } from './call.js';
|
|
19
20
|
const STATES = ['pending', 'running', 'parked', 'error'];
|
|
20
21
|
function box(raw, id) {
|
|
@@ -54,7 +55,7 @@ async function up(t, id) {
|
|
|
54
55
|
b = await sandbox(t, id);
|
|
55
56
|
}
|
|
56
57
|
if (b.state !== 'running')
|
|
57
|
-
throw new Refusal(503, b.error || 'The sandbox did not start again. Try again.');
|
|
58
|
+
throw new Refusal(503, b.error || phrase('The sandbox did not start again. Try again.'));
|
|
58
59
|
}
|
|
59
60
|
/** Asks what needs a running sandbox; a parked one is resumed first. */
|
|
60
61
|
async function running(t, id, ask) {
|
|
@@ -75,7 +76,7 @@ export async function door(t, id, which, session = '') {
|
|
|
75
76
|
const r = await running(t, id, () => call(t, 'POST', `/v1/sandbox/${seg(id)}/${which}/ticket`));
|
|
76
77
|
const path = typeof r?.url === 'string' ? r.url : '';
|
|
77
78
|
if (!path.startsWith('/v1/'))
|
|
78
|
-
throw new Refusal(502, 'The sandbox answered with no address to open');
|
|
79
|
+
throw new Refusal(502, phrase('The sandbox answered with no address to open'));
|
|
79
80
|
const arg = which === 'terminal' && NAME.test(session) ? `&arg=${session}` : '';
|
|
80
81
|
return `${t.api}${path}${arg}`;
|
|
81
82
|
}
|
|
@@ -104,10 +105,10 @@ export async function preview(t, id, port) {
|
|
|
104
105
|
url = new URL(typeof r?.url === 'string' ? r.url : '');
|
|
105
106
|
}
|
|
106
107
|
catch {
|
|
107
|
-
throw new Refusal(502, 'The sandbox answered with no address to open');
|
|
108
|
+
throw new Refusal(502, phrase('The sandbox answered with no address to open'));
|
|
108
109
|
}
|
|
109
110
|
if (url.protocol !== 'https:')
|
|
110
|
-
throw new Refusal(502, 'The sandbox answered with no address to open');
|
|
111
|
+
throw new Refusal(502, phrase('The sandbox answered with no address to open'));
|
|
111
112
|
return url.href;
|
|
112
113
|
}
|
|
113
114
|
/** What a file shows at most. */
|
package/lib/api/sessions.d.ts
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runs, as the platform records them: one session per run.
|
|
3
|
-
*
|
|
4
|
-
* GET /v1/agent/sessions?kind=&project=&status=&limit=&after= newest first, {sessions, next}
|
|
5
|
-
* GET /v1/agent/sessions/{id} + the 50 most recent events
|
|
6
|
-
* PATCH /v1/agent/sessions/{id} {title}|{published} rename it; open its story to the public build route
|
|
7
|
-
* POST /v1/agent/sessions/{id}/message {message} steer a running run
|
|
8
|
-
* POST /v1/agent/sessions/{id}/pause pause it; a sandbox run keeps its work on its branch
|
|
9
|
-
* POST /v1/agent/sessions/{id}/resume ask a paused run to go on
|
|
10
|
-
* POST /v1/agent/sessions/{id}/stop {message} end it, work kept
|
|
11
|
-
* GET /v1/agent/sessions/stream?root={id} SSE: `session` and `event` frames,
|
|
12
|
-
* each wrapped: {"session":{…}}, {"event":{…}}
|
|
13
|
-
*
|
|
14
|
-
* The stream is best-effort by the platform's own statement: a subscriber that
|
|
15
|
-
* falls 256 frames behind is dropped. So `watch` reconnects, and every
|
|
16
|
-
* reconnect is reported, which is the caller's cue to re-read the detail.
|
|
17
|
-
*/
|
|
18
1
|
import { type Target } from './call.ts';
|
|
19
2
|
export type Status = 'running' | 'paused' | 'done' | 'error' | 'stopped' | string;
|
|
20
3
|
export interface Session {
|