@hanzo/build 0.2.40 → 0.2.42

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/lib/ago.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** `just now`, `5m ago`, `3h ago`, `4d ago`, else the date, for an RFC 3339 instant; `—` for none. */
2
+ export declare function ago(iso: string): string;
package/lib/ago.js ADDED
@@ -0,0 +1,18 @@
1
+ /** `just now`, `5m ago`, `3h ago`, `4d ago`, else the date, for an RFC 3339 instant; `—` for none. */
2
+ export function ago(iso) {
3
+ const t = Date.parse(iso);
4
+ if (!Number.isFinite(t))
5
+ return '—';
6
+ const minutes = Math.round((Date.now() - t) / 60000);
7
+ if (minutes < 1)
8
+ return 'just now';
9
+ if (minutes < 60)
10
+ return `${minutes}m ago`;
11
+ const hours = Math.round(minutes / 60);
12
+ if (hours < 24)
13
+ return `${hours}h ago`;
14
+ const days = Math.round(hours / 24);
15
+ if (days < 30)
16
+ return `${days}d ago`;
17
+ return new Date(t).toLocaleDateString();
18
+ }
package/lib/api/auto.d.ts CHANGED
@@ -1,27 +1,86 @@
1
1
  /**
2
- * Automations: flows that run themselves.
2
+ * Automations: a task that runs itself, on a schedule or when asked.
3
3
  *
4
- * GET /v1/auto/flows {data: [flow]} newest updated first
5
- * GET /v1/auto/flows/{id} the flow and its latest version (the name)
6
- * POST /v1/auto/flows a disabled draft
7
- * POST /v1/auto/flows/{id}/enable
8
- * POST /v1/auto/flows/{id}/disable
4
+ * GET /v1/auto/automations {data: [automation]}
5
+ * POST /v1/auto/automations create one: name and instructions
6
+ * GET /v1/auto/automations/{id} one
7
+ * PATCH /v1/auto/automations/{id} change it, or switch it on or off
8
+ * DELETE /v1/auto/automations/{id} delete it, its schedule and its runs
9
+ * POST /v1/auto/automations/{id}/run {run: {id, status}} one run, now
10
+ * GET /v1/auto/automations/{id}/runs {data: [run]} newest first
9
11
  *
10
- * The list page records the id and whether the trigger is armed. The name lives
11
- * on the latest version, so each row is read once more.
12
+ * Each run is one Dev run in this organization, started as the person who last
13
+ * saved what it does. A draft is a flow named and not yet told what to do: it
14
+ * runs once its instructions are saved.
12
15
  */
13
16
  import { type Target } from './call.ts';
14
- export interface Automation {
15
- id: string;
17
+ export type Kind = 'manual' | 'hourly' | 'daily' | 'weekdays' | 'weekly' | 'cron';
18
+ export type Permissions = 'ask' | 'auto';
19
+ /** When it runs, in the person's own words and zone. */
20
+ export interface Schedule {
21
+ kind: Kind;
22
+ /** HH:MM, 24-hour, for daily, weekdays and weekly; 00:MM for hourly. */
23
+ at?: string;
24
+ /** mon … sun, for weekly. */
25
+ day?: string;
26
+ /** Five fields, for cron. */
27
+ cron?: string;
28
+ /** An IANA zone; the platform reads an absent one as UTC. */
29
+ tz?: string;
30
+ }
31
+ /** What an automation is: what the editor holds and the platform keeps. */
32
+ export interface Draft {
16
33
  name: string;
17
- /** ENABLED or DISABLED. */
34
+ instructions: string;
35
+ /** A model id, or null for Enso, the default. */
36
+ model: string | null;
37
+ schedule: Schedule;
38
+ permissions: Permissions;
39
+ notify: boolean;
40
+ enabled: boolean;
41
+ }
42
+ export interface Last {
43
+ id: string;
18
44
  status: string;
19
- /** Unix milliseconds, or 0. */
20
- updated: number;
45
+ at: string;
46
+ summary: string;
47
+ }
48
+ export interface Automation extends Draft {
49
+ id: string;
50
+ project: string | null;
51
+ /** Named and not yet told what to do: it never runs until its instructions are saved. */
52
+ draft: boolean;
53
+ /** When it runs next, RFC 3339, or null when it runs only when asked or is off. */
54
+ next: string | null;
55
+ last: Last | null;
56
+ created: string;
57
+ updated: string;
21
58
  }
22
- export declare function automation(raw: unknown): Automation | null;
23
- export declare function flows(t: Target): Promise<Automation[]>;
24
- /** A disabled draft. Creating it does not arm the trigger. */
25
- export declare function add(t: Target, name: string): Promise<Automation>;
26
- /** Arm or disarm the trigger. A disarmed flow can still be started on demand. */
27
- export declare function arm(t: Target, id: string, on: boolean): Promise<void>;
59
+ /** One run: succeeded, failed, running, queued, skipped or refused. */
60
+ export interface Run {
61
+ id: string;
62
+ status: string;
63
+ at: string;
64
+ finished: string | null;
65
+ summary: string;
66
+ /** The Dev run it started, or null when it started none. */
67
+ session: string | null;
68
+ }
69
+ /** One automation as the platform answers it, or null for a row with no id. */
70
+ export declare function read(raw: unknown): Automation | null;
71
+ export declare function automations(t: Target): Promise<Automation[]>;
72
+ export declare function automation(t: Target, id: string): Promise<Automation>;
73
+ export declare function create(t: Target, d: Draft): Promise<Automation>;
74
+ /** What changed between two drafts, field by field: what a save sends. */
75
+ export declare function changes(was: Draft, now: Draft): Partial<Draft>;
76
+ /** Change an automation. A change to anything but `enabled` makes the caller the person it runs as. */
77
+ export declare function save(t: Target, id: string, change: Partial<Draft>): Promise<Automation>;
78
+ export declare function remove(t: Target, id: string): Promise<void>;
79
+ /** Start one run now. While one is going, the platform records this start as skipped. */
80
+ export declare function start(t: Target, id: string): Promise<{
81
+ id: string;
82
+ status: string;
83
+ }>;
84
+ export declare function runs(t: Target, id: string): Promise<Run[]>;
85
+ /** A schedule the way a person says it. */
86
+ export declare function words(s: Schedule): string;
package/lib/api/auto.js CHANGED
@@ -1,67 +1,127 @@
1
1
  /**
2
- * Automations: flows that run themselves.
2
+ * Automations: a task that runs itself, on a schedule or when asked.
3
3
  *
4
- * GET /v1/auto/flows {data: [flow]} newest updated first
5
- * GET /v1/auto/flows/{id} the flow and its latest version (the name)
6
- * POST /v1/auto/flows a disabled draft
7
- * POST /v1/auto/flows/{id}/enable
8
- * POST /v1/auto/flows/{id}/disable
4
+ * GET /v1/auto/automations {data: [automation]}
5
+ * POST /v1/auto/automations create one: name and instructions
6
+ * GET /v1/auto/automations/{id} one
7
+ * PATCH /v1/auto/automations/{id} change it, or switch it on or off
8
+ * DELETE /v1/auto/automations/{id} delete it, its schedule and its runs
9
+ * POST /v1/auto/automations/{id}/run {run: {id, status}} one run, now
10
+ * GET /v1/auto/automations/{id}/runs {data: [run]} newest first
9
11
  *
10
- * The list page records the id and whether the trigger is armed. The name lives
11
- * on the latest version, so each row is read once more.
12
+ * Each run is one Dev run in this organization, started as the person who last
13
+ * saved what it does. A draft is a flow named and not yet told what to do: it
14
+ * runs once its instructions are saved.
12
15
  */
13
16
  import { call, seg } from './call.js';
14
17
  const str = (v) => (typeof v === 'string' ? v : '');
15
- const obj = (v) => v && typeof v === 'object' ? v : {};
16
- export function automation(raw) {
18
+ const nullable = (v) => (typeof v === 'string' && v ? v : null);
19
+ const obj = (v) => (v && typeof v === 'object' ? v : {});
20
+ const KINDS = ['manual', 'hourly', 'daily', 'weekdays', 'weekly', 'cron'];
21
+ function schedule(raw) {
22
+ const o = obj(raw);
23
+ const kind = KINDS.includes(o.kind) ? o.kind : 'manual';
24
+ const out = { kind };
25
+ for (const k of ['at', 'day', 'cron', 'tz'])
26
+ if (str(o[k]))
27
+ out[k] = str(o[k]);
28
+ return out;
29
+ }
30
+ /** One automation as the platform answers it, or null for a row with no id. */
31
+ export function read(raw) {
17
32
  const o = obj(raw);
18
33
  const id = str(o.id);
19
34
  if (!id)
20
35
  return null;
21
- const version = obj(o.version);
36
+ const last = obj(o.last);
22
37
  return {
23
38
  id,
24
- name: str(version.displayName) || str(o.displayName) || id,
25
- status: str(o.status) || 'DISABLED',
26
- updated: typeof o.updated === 'number' ? o.updated : 0,
39
+ name: str(o.name) || id,
40
+ instructions: str(o.instructions),
41
+ project: nullable(o.project),
42
+ model: nullable(o.model),
43
+ schedule: schedule(o.schedule),
44
+ permissions: o.permissions === 'auto' ? 'auto' : 'ask',
45
+ notify: o.notify === true,
46
+ enabled: o.enabled === true,
47
+ draft: o.draft === true,
48
+ next: nullable(o.next),
49
+ last: str(last.id) ? { id: str(last.id), status: str(last.status), at: str(last.at), summary: str(last.summary) } : null,
50
+ created: str(o.created),
51
+ updated: str(o.updated),
27
52
  };
28
53
  }
29
- export async function flows(t) {
30
- const raw = await call(t, 'GET', '/v1/auto/flows');
31
- const data = Array.isArray(obj(raw).data) ? obj(raw).data : [];
32
- const rows = data.map(automation).filter((a) => a !== null).slice(0, 40);
33
- return Promise.all(rows.map(async (a) => {
34
- if (a.name !== a.id)
35
- return a;
36
- try {
37
- return automation(await call(t, 'GET', `/v1/auto/flows/${seg(a.id)}`)) ?? a;
38
- }
39
- catch {
40
- return a;
41
- }
42
- }));
54
+ /** The Dev run a transcript link opens: `…/dev?run=sess_…`. */
55
+ function session(link) {
56
+ const m = str(link).match(/[?&]run=(sess_[0-9a-f]{32})\b/);
57
+ return m ? m[1] : null;
58
+ }
59
+ const list = (raw) => (Array.isArray(obj(raw).data) ? obj(raw).data : []);
60
+ export async function automations(t) {
61
+ return list(await call(t, 'GET', '/v1/auto/automations'))
62
+ .map(read)
63
+ .filter((a) => a !== null);
64
+ }
65
+ export async function automation(t, id) {
66
+ const a = read(await call(t, 'GET', `/v1/auto/automations/${seg(id)}`));
67
+ if (!a)
68
+ throw new Error('The automation came back without an id');
69
+ return a;
70
+ }
71
+ export async function create(t, d) {
72
+ const a = read(await call(t, 'POST', '/v1/auto/automations', d));
73
+ if (!a)
74
+ throw new Error('The automation was created and came back without an id');
75
+ return a;
76
+ }
77
+ /** What changed between two drafts, field by field: what a save sends. */
78
+ export function changes(was, now) {
79
+ const out = {};
80
+ for (const k of Object.keys(now)) {
81
+ if (JSON.stringify(was[k]) !== JSON.stringify(now[k]))
82
+ out[k] = now[k];
83
+ }
84
+ return out;
85
+ }
86
+ /** Change an automation. A change to anything but `enabled` makes the caller the person it runs as. */
87
+ export async function save(t, id, change) {
88
+ const a = read(await call(t, 'PATCH', `/v1/auto/automations/${seg(id)}`, change));
89
+ if (!a)
90
+ throw new Error('The automation was saved and came back without an id');
91
+ return a;
92
+ }
93
+ export async function remove(t, id) {
94
+ await call(t, 'DELETE', `/v1/auto/automations/${seg(id)}`);
95
+ }
96
+ /** Start one run now. While one is going, the platform records this start as skipped. */
97
+ export async function start(t, id) {
98
+ const run = obj(obj(await call(t, 'POST', `/v1/auto/automations/${seg(id)}/run`)).run);
99
+ return { id: str(run.id), status: str(run.status) };
43
100
  }
44
- /** A disabled draft. Creating it does not arm the trigger. */
45
- export async function add(t, name) {
46
- const displayName = name.trim();
47
- if (!displayName)
48
- throw new Error('Name the automation');
49
- const raw = await call(t, 'POST', '/v1/auto/flows', {
50
- displayName,
51
- trigger: {
52
- name: 'trigger',
53
- type: 'PIECE_TRIGGER',
54
- displayName: 'Start',
55
- strategy: 'MANUAL',
56
- settings: { pieceName: 'core', triggerName: 'manual' },
57
- },
58
- });
59
- const row = automation(raw);
60
- if (!row)
61
- throw new Error('The automation was created and did not come back named');
62
- return row;
101
+ export async function runs(t, id) {
102
+ return list(await call(t, 'GET', `/v1/auto/automations/${seg(id)}/runs`))
103
+ .map((raw) => {
104
+ const o = obj(raw);
105
+ return { id: str(o.id), status: str(o.status), at: str(o.at), finished: nullable(o.finished), summary: str(o.summary), session: session(o.transcript) };
106
+ })
107
+ .filter((r) => r.id);
63
108
  }
64
- /** Arm or disarm the trigger. A disarmed flow can still be started on demand. */
65
- export async function arm(t, id, on) {
66
- await call(t, 'POST', `/v1/auto/flows/${seg(id)}/${on ? 'enable' : 'disable'}`);
109
+ const DAYS = { mon: 'Mondays', tue: 'Tuesdays', wed: 'Wednesdays', thu: 'Thursdays', fri: 'Fridays', sat: 'Saturdays', sun: 'Sundays' };
110
+ /** A schedule the way a person says it. */
111
+ export function words(s) {
112
+ const zone = ` · ${s.tz || 'UTC'}`;
113
+ switch (s.kind) {
114
+ case 'manual':
115
+ return 'When you run it';
116
+ case 'hourly':
117
+ return `Every hour at :${(s.at ?? '00:00').slice(3) || '00'}`;
118
+ case 'daily':
119
+ return `Every day at ${s.at}${zone}`;
120
+ case 'weekdays':
121
+ return `Weekdays at ${s.at}${zone}`;
122
+ case 'weekly':
123
+ return `${DAYS[s.day ?? ''] ?? s.day} at ${s.at}${zone}`;
124
+ case 'cron':
125
+ return `Cron ${s.cron}${zone}`;
126
+ }
67
127
  }
@@ -1,14 +1,17 @@
1
+ /**
2
+ * The models a run can be routed through, from the platform's own catalog.
3
+ *
4
+ * GET /v1/models → {data: [{id, class, family, …}]}
5
+ *
6
+ * Read by `@hanzo/ui/models` `parseModels`, so a model's class and family are
7
+ * the gateway's fields, never guessed from its id. `ENSO` is the router: it
8
+ * picks a model per step, which is why it is the default, and a run that names
9
+ * it names no model at all.
10
+ */
11
+ import { type ModelCatalogEntry } from '@hanzo/ui/models/catalog';
1
12
  import { type Target } from './call.ts';
2
- export interface Model {
3
- id: string;
4
- label: string;
5
- /** Listed and never chosen. */
6
- disabled?: boolean;
7
- hint?: string;
8
- /** Where a person asks for access to a model they cannot call. */
9
- request?: string;
10
- }
11
- export declare const ENSO = "enso";
12
- /** A readable name for an id: `anthropic/claude-opus-5.5` → `Claude Opus 5.5`, `zen5-coder` → `Zen5 Coder`. */
13
- export declare function label(id: string): string;
13
+ export { ENSO } from '@hanzo/ui/models/catalog';
14
+ export type Model = ModelCatalogEntry;
14
15
  export declare function models(t: Target): Promise<Model[]>;
16
+ /** The payer's plan usage, as `GET /v1/ai/limits` answers it: what a picker marks paused. */
17
+ export declare function limits(t: Target, signal?: AbortSignal): Promise<unknown>;
package/lib/api/models.js CHANGED
@@ -1,42 +1,20 @@
1
1
  /**
2
2
  * The models a run can be routed through, from the platform's own catalog.
3
3
  *
4
- * GET /v1/models → {data: [{id, …}]}
4
+ * GET /v1/models → {data: [{id, class, family, …}]}
5
5
  *
6
- * `enso` is the router: it picks a model per step, which is why it is the
7
- * default and why it is not a model a person has to understand to use.
8
- *
9
- * After what the gateway serves come the models nobody can call yet, from
10
- * @hanzo/ui's `RESEARCH`: listed so a person knows they exist, disabled so no
11
- * run is ever started on one, with where to ask for access.
6
+ * Read by `@hanzo/ui/models` `parseModels`, so a model's class and family are
7
+ * the gateway's fields, never guessed from its id. `ENSO` is the router: it
8
+ * picks a model per step, which is why it is the default, and a run that names
9
+ * it names no model at all.
12
10
  */
13
- import { RESEARCH } from '@hanzo/ui/models/catalog';
11
+ import { parseModels } from '@hanzo/ui/models/catalog';
14
12
  import { call } from './call.js';
15
- export const ENSO = 'enso';
16
- /** A readable name for an id: `anthropic/claude-opus-5.5` → `Claude Opus 5.5`, `zen5-coder` → `Zen5 Coder`. */
17
- export function label(id) {
18
- const tail = id.slice(id.lastIndexOf('/') + 1).replace(/:.*$/, '');
19
- return tail
20
- .split('-')
21
- .filter(Boolean)
22
- .map((w) => (/^\d/.test(w) ? w : w[0].toUpperCase() + w.slice(1)))
23
- .join(' ');
24
- }
13
+ export { ENSO } from '@hanzo/ui/models/catalog';
25
14
  export async function models(t) {
26
- const raw = await call(t, 'GET', '/v1/models');
27
- const rows = Array.isArray(raw?.data) ? raw.data : [];
28
- const ids = rows
29
- .map((r) => (r && typeof r === 'object' && typeof r.id === 'string' ? r.id : ''))
30
- .filter((id) => id && !id.endsWith(':batch'));
31
- const out = [...new Set([ENSO, ...ids])];
32
- return [
33
- ...out.map((id) => ({ id, label: label(id) })),
34
- ...RESEARCH.filter((m) => !out.includes(m.id)).map((m) => ({
35
- id: m.id,
36
- label: m.label ?? label(m.id),
37
- disabled: true,
38
- hint: 'Research preview',
39
- request: m.request,
40
- })),
41
- ];
15
+ return parseModels(await call(t, 'GET', '/v1/models'));
16
+ }
17
+ /** The payer's plan usage, as `GET /v1/ai/limits` answers it: what a picker marks paused. */
18
+ export function limits(t, signal) {
19
+ return call(t, 'GET', '/v1/ai/limits', undefined, { signal });
42
20
  }
@@ -0,0 +1,5 @@
1
+ export declare function Automations(): import("react").JSX.Element;
2
+ /** One automation: what it does and when, its switch, Run now, and its runs. `new` writes one. */
3
+ export declare function Editor({ id }: {
4
+ id: string;
5
+ }): import("react").JSX.Element;