railcode 0.1.22 → 0.1.24

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.
@@ -0,0 +1,220 @@
1
+ // Pure helpers behind `railcode logs` (observability streams) and
2
+ // `railcode analytics` (per-app pageviews). No I/O or process state — the command
3
+ // layer in index.ts does the HTTP and printing. Mirrors connectors.ts / db.ts.
4
+ import { formatTable } from "./db.js";
5
+ export class ObservabilityError extends Error {
6
+ }
7
+ function fmtTime(iso) {
8
+ if (!iso)
9
+ return "-";
10
+ return iso.slice(0, 16).replace("T", " ");
11
+ }
12
+ function num(value) {
13
+ return value === null || value === undefined ? "-" : String(value);
14
+ }
15
+ function str(value) {
16
+ return value === null || value === undefined ? "-" : String(value);
17
+ }
18
+ function refLabel(ref) {
19
+ if (!ref)
20
+ return "-";
21
+ return ref.email ?? ref.name ?? ref.uuid;
22
+ }
23
+ export const LOG_STREAMS = [
24
+ {
25
+ key: "connector",
26
+ aliases: ["connectors", "sql", "connection", "connections", "db"],
27
+ path: "/connections/logs",
28
+ label: "data-connection SQL queries",
29
+ capability: "connection:manage",
30
+ filters: ["app", "user", "connector", "status"],
31
+ statuses: ["success", "error"],
32
+ columns: ["request_id", "connector", "engine", "status", "ms", "rows", "user", "when"],
33
+ map: (r) => [
34
+ str(r.request_id),
35
+ str(r.connection_name),
36
+ str(r.engine),
37
+ str(r.status),
38
+ num(r.duration_ms),
39
+ num(r.rowcount),
40
+ refLabel(r.user),
41
+ fmtTime(r.created_at),
42
+ ],
43
+ },
44
+ {
45
+ key: "service-connector",
46
+ aliases: ["service-connectors", "sc", "proxy"],
47
+ path: "/service-connectors/logs",
48
+ label: "service-connector HTTP proxy calls",
49
+ capability: "service-connector:manage",
50
+ filters: ["app", "user", "connector", "status"],
51
+ statuses: ["success", "error"],
52
+ columns: ["request_id", "connector", "method", "path", "status", "resp", "ms", "when"],
53
+ map: (r) => [
54
+ str(r.request_id),
55
+ str(r.connector_name),
56
+ str(r.method),
57
+ str(r.path),
58
+ str(r.status),
59
+ num(r.response_status),
60
+ num(r.duration_ms),
61
+ fmtTime(r.created_at),
62
+ ],
63
+ },
64
+ {
65
+ key: "llm",
66
+ aliases: ["ai"],
67
+ path: "/llm/logs",
68
+ label: "LLM gateway calls",
69
+ capability: "llm:manage",
70
+ filters: ["app", "user", "status", "source"],
71
+ statuses: ["success", "provider_error", "validation_error", "cancelled", "timeout"],
72
+ sources: ["app", "agent"],
73
+ columns: ["request_id", "provider", "model", "status", "in_tok", "out_tok", "ms", "when"],
74
+ map: (r) => [
75
+ str(r.request_id),
76
+ str(r.provider),
77
+ str(r.model),
78
+ str(r.status),
79
+ num(r.input_tokens),
80
+ num(r.output_tokens),
81
+ num(r.latency_ms),
82
+ fmtTime(r.created_at),
83
+ ],
84
+ },
85
+ {
86
+ key: "email",
87
+ aliases: ["emails", "mail"],
88
+ path: "/email/logs",
89
+ label: "email gateway sends",
90
+ capability: "email:manage",
91
+ filters: ["app", "user", "status"],
92
+ statuses: ["success", "provider_error", "validation_error", "suppressed"],
93
+ columns: ["request_id", "subject", "recipients", "status", "when"],
94
+ map: (r) => [
95
+ str(r.request_id),
96
+ str(r.subject),
97
+ num(r.recipient_count),
98
+ str(r.status),
99
+ fmtTime(r.created_at),
100
+ ],
101
+ },
102
+ {
103
+ key: "agent",
104
+ aliases: ["agents", "runs"],
105
+ path: "/agents/runs",
106
+ label: "managed-agent runs",
107
+ capability: "agent:manage",
108
+ filters: ["agent", "app", "user", "status"],
109
+ statuses: ["running", "success", "failed", "cancelled", "limit_exceeded"],
110
+ columns: ["request_id", "agent", "status", "trigger", "started", "finished"],
111
+ map: (r) => [
112
+ str(r.request_id),
113
+ refLabel(r.agent),
114
+ str(r.status),
115
+ str(r.trigger_type),
116
+ fmtTime(r.started_at),
117
+ fmtTime(r.finished_at),
118
+ ],
119
+ },
120
+ ];
121
+ export function resolveLogStream(name) {
122
+ const needle = (name ?? "").trim().toLowerCase();
123
+ if (!needle) {
124
+ throw new ObservabilityError(`Pick a log stream: ${LOG_STREAMS.map((s) => s.key).join(", ")}.`);
125
+ }
126
+ const found = LOG_STREAMS.find((s) => s.key === needle || s.aliases.includes(needle));
127
+ if (!found) {
128
+ throw new ObservabilityError(`Unknown log stream "${name}". One of: ${LOG_STREAMS.map((s) => s.key).join(", ")}.`);
129
+ }
130
+ return found;
131
+ }
132
+ export function validateLogStatus(stream, value) {
133
+ const v = value.trim().toLowerCase();
134
+ if (!stream.statuses.includes(v)) {
135
+ throw new ObservabilityError(`--status for ${stream.key} must be one of: ${stream.statuses.join(", ")}.`);
136
+ }
137
+ return v;
138
+ }
139
+ export function validateLogSource(stream, value) {
140
+ const v = value.trim().toLowerCase();
141
+ if (!stream.sources || !stream.sources.includes(v)) {
142
+ throw new ObservabilityError(`--source is only valid for llm logs (${(stream.sources ?? []).join(", ") || "n/a"}).`);
143
+ }
144
+ return v;
145
+ }
146
+ export function parseLimit(value) {
147
+ if (value === undefined)
148
+ return undefined;
149
+ const n = Number(value);
150
+ if (!Number.isInteger(n) || n < 1 || n > 500) {
151
+ throw new ObservabilityError("--limit must be an integer between 1 and 500.");
152
+ }
153
+ return n;
154
+ }
155
+ // Build the `GET /logs` query string from resolved filter values (app/user/agent
156
+ // are already UUIDs). Only non-empty values are appended; order is stable for tests.
157
+ export function buildLogQuery(params) {
158
+ const qs = new URLSearchParams();
159
+ if (params.agent)
160
+ qs.set("agent", params.agent);
161
+ if (params.app)
162
+ qs.set("app", params.app);
163
+ if (params.user)
164
+ qs.set("user", params.user);
165
+ if (params.connector)
166
+ qs.set("connector", params.connector);
167
+ if (params.source)
168
+ qs.set("source", params.source);
169
+ if (params.status)
170
+ qs.set("status", params.status);
171
+ if (params.limit !== undefined)
172
+ qs.set("limit", String(params.limit));
173
+ const s = qs.toString();
174
+ return s ? `?${s}` : "";
175
+ }
176
+ export function logTable(stream, rows) {
177
+ return formatTable(stream.columns, rows.map((r) => stream.map(r)));
178
+ }
179
+ // ── Analytics (per-app pageviews) ───────────────────────────────────────────
180
+ export const ANALYTICS_RANGES = ["1d", "7d", "30d", "90d"];
181
+ export function parseRange(value) {
182
+ if (value === undefined)
183
+ return undefined;
184
+ const v = value.trim().toLowerCase();
185
+ if (!ANALYTICS_RANGES.includes(v)) {
186
+ throw new ObservabilityError(`--range must be one of: ${ANALYTICS_RANGES.join(", ")}.`);
187
+ }
188
+ return v;
189
+ }
190
+ export function renderAnalytics(slug, data) {
191
+ const lines = [];
192
+ lines.push(`Analytics for ${slug} — ${data.range} (${data.from} → ${data.to})`);
193
+ lines.push(` views: ${data.totals.views} unique users: ${data.totals.uniques}`);
194
+ lines.push("");
195
+ if (data.daily.length > 0) {
196
+ lines.push("Daily");
197
+ lines.push(indent(formatTable(["day", "views", "uniques"], data.daily.map((d) => [d.day, String(d.views), String(d.uniques)]))));
198
+ lines.push("");
199
+ }
200
+ if (data.top_paths.length > 0) {
201
+ lines.push("Top paths");
202
+ lines.push(indent(formatTable(["path", "views"], data.top_paths.map((p) => [p.path, String(p.views)]))));
203
+ lines.push("");
204
+ }
205
+ if (data.users.length > 0) {
206
+ lines.push("Top users");
207
+ lines.push(indent(formatTable(["user", "views"], data.users.map((u) => [u.display_name, String(u.views)]))));
208
+ lines.push("");
209
+ }
210
+ if (data.totals.views === 0) {
211
+ lines.push("No pageviews recorded in this window.");
212
+ }
213
+ return lines.join("\n").replace(/\n+$/, "");
214
+ }
215
+ function indent(block) {
216
+ return block
217
+ .split("\n")
218
+ .map((l) => ` ${l}`)
219
+ .join("\n");
220
+ }
@@ -0,0 +1,227 @@
1
+ // Pure helpers behind the org-admin CLI surfaces — `railcode members`,
2
+ // `railcode roles`, and the access half of `railcode apps`. Kept free of any I/O
3
+ // or process state so they can be unit-tested directly (see test/orgadmin.test.mjs);
4
+ // the command layer in index.ts does the HTTP and printing. Mirrors connectors.ts.
5
+ import { formatTable } from "./db.js";
6
+ export class OrgAdminError extends Error {
7
+ }
8
+ // Trim an ISO 8601 timestamp to a compact "YYYY-MM-DD HH:MM" for table display.
9
+ export function fmtTime(iso) {
10
+ if (!iso)
11
+ return "-";
12
+ return iso.slice(0, 16).replace("T", " ");
13
+ }
14
+ function splitList(value) {
15
+ return value
16
+ .split(",")
17
+ .map((part) => part.trim())
18
+ .filter((part) => part.length > 0);
19
+ }
20
+ export function memberTable(members) {
21
+ return formatTable(["email", "name", "role", "verified"], members.map((m) => [m.email, m.name, m.role, m.email_verified ? "yes" : "no"]));
22
+ }
23
+ // Resolve a member reference — a UUID or an email (case-insensitive) — against a
24
+ // fetched member list. Throws OrgAdminError when it matches nothing/ambiguously.
25
+ export function resolveMemberRef(members, ref) {
26
+ const needle = ref.trim();
27
+ if (!needle)
28
+ throw new OrgAdminError("Expected a member (email or UUID).");
29
+ const byUuid = members.find((m) => m.uuid === needle);
30
+ if (byUuid)
31
+ return byUuid;
32
+ const lower = needle.toLowerCase();
33
+ const byEmail = members.filter((m) => m.email.toLowerCase() === lower);
34
+ if (byEmail.length === 1)
35
+ return byEmail[0];
36
+ if (byEmail.length > 1) {
37
+ throw new OrgAdminError(`Multiple members match "${ref}" — use the UUID.`);
38
+ }
39
+ throw new OrgAdminError(`No member matches "${ref}" (try an email or UUID).`);
40
+ }
41
+ // The two assignable system roles (the owner tier can't be assigned/reassigned via
42
+ // the API — it's the org creator, transferred through a separate flow).
43
+ export function parseAssignableRole(value) {
44
+ const v = (value ?? "").trim().toLowerCase();
45
+ if (v === "admin" || v === "member")
46
+ return v;
47
+ throw new OrgAdminError('--role must be "admin" or "member".');
48
+ }
49
+ export function roleTable(roles) {
50
+ return formatTable(["name", "members", "description", "uuid"], roles.map((r) => [
51
+ r.name,
52
+ String(r.member_uuids.length),
53
+ r.description || "-",
54
+ r.uuid,
55
+ ]));
56
+ }
57
+ // Resolve an org-role reference — a UUID or a name (case-insensitive) — against a
58
+ // fetched role list.
59
+ export function resolveRoleRef(roles, ref) {
60
+ const needle = ref.trim();
61
+ if (!needle)
62
+ throw new OrgAdminError("Expected a role (name or UUID).");
63
+ const byUuid = roles.find((r) => r.uuid === needle);
64
+ if (byUuid)
65
+ return byUuid;
66
+ const lower = needle.toLowerCase();
67
+ const byName = roles.filter((r) => r.name.toLowerCase() === lower);
68
+ if (byName.length === 1)
69
+ return byName[0];
70
+ if (byName.length > 1) {
71
+ throw new OrgAdminError(`Multiple roles named "${ref}" — use the UUID.`);
72
+ }
73
+ throw new OrgAdminError(`No role matches "${ref}" (try a name or UUID).`);
74
+ }
75
+ // How the subject label is resolved for display: org → "everyone"; role/user/app →
76
+ // looked up by uuid in the provided maps, else the raw type.
77
+ export function grantTable(grants, roleNames, userEmails, appSlugs = new Map()) {
78
+ return formatTable(["id", "subject", "action", "resource_type", "resource_id"], grants.map((g) => [
79
+ String(g.id),
80
+ grantSubjectLabel(g, roleNames, userEmails, appSlugs),
81
+ g.action,
82
+ g.resource_type,
83
+ g.resource_id,
84
+ ]));
85
+ }
86
+ export function grantSubjectLabel(g, roleNames, userEmails, appSlugs = new Map()) {
87
+ if (g.subject_type === "org")
88
+ return "everyone";
89
+ if (!g.subject_uuid)
90
+ return g.subject_type;
91
+ if (g.subject_type === "role")
92
+ return `role:${roleNames.get(g.subject_uuid) ?? g.subject_uuid}`;
93
+ if (g.subject_type === "user")
94
+ return `user:${userEmails.get(g.subject_uuid) ?? g.subject_uuid}`;
95
+ if (g.subject_type === "app")
96
+ return `app:${appSlugs.get(g.subject_uuid) ?? g.subject_uuid}`;
97
+ return `${g.subject_type}:${g.subject_uuid}`;
98
+ }
99
+ export function parseGrantSubject(value) {
100
+ const raw = (value ?? "").trim();
101
+ if (!raw) {
102
+ throw new OrgAdminError("--subject is required: org | role:<name|uuid> | user:<email|uuid>.");
103
+ }
104
+ if (raw.toLowerCase() === "org" || raw.toLowerCase() === "everyone")
105
+ return { kind: "org" };
106
+ const idx = raw.indexOf(":");
107
+ if (idx === -1) {
108
+ throw new OrgAdminError(`Bad --subject "${value}". Use org | role:<name|uuid> | user:<email|uuid>.`);
109
+ }
110
+ const kind = raw.slice(0, idx).toLowerCase();
111
+ const ref = raw.slice(idx + 1).trim();
112
+ if (!ref)
113
+ throw new OrgAdminError(`--subject ${kind}: is missing a value.`);
114
+ if (kind === "role")
115
+ return { kind: "role", ref };
116
+ if (kind === "user")
117
+ return { kind: "user", ref };
118
+ throw new OrgAdminError(`Unknown --subject kind "${kind}". Use org, role: or user:.`);
119
+ }
120
+ // The grant resource types the management API accepts (the implied action is
121
+ // stored server-side). `app` grants are manifest-managed, not settable here.
122
+ export const GRANT_RESOURCE_TYPES = [
123
+ "app",
124
+ "llm",
125
+ "email",
126
+ "sc_endpoint",
127
+ "connector",
128
+ "saved_query",
129
+ "agent",
130
+ ];
131
+ export function parseResourceType(value) {
132
+ const v = (value ?? "").trim();
133
+ if (!v) {
134
+ throw new OrgAdminError(`--resource is required (${GRANT_RESOURCE_TYPES.join(" | ")}).`);
135
+ }
136
+ if (!GRANT_RESOURCE_TYPES.includes(v)) {
137
+ throw new OrgAdminError(`Unknown --resource "${value}". One of: ${GRANT_RESOURCE_TYPES.join(", ")}.`);
138
+ }
139
+ return v;
140
+ }
141
+ export function parseResourceIds(value) {
142
+ const ids = value === undefined ? [] : splitList(value);
143
+ if (ids.length === 0) {
144
+ throw new OrgAdminError('--ids is required (comma-separated, e.g. "*" or a name).');
145
+ }
146
+ return ids;
147
+ }
148
+ export function renderEffective(eff, who) {
149
+ const lines = [`Effective access for ${who}`];
150
+ if (eff.admin) {
151
+ lines.push(" admin/owner tier — full access to everything (bypasses grants).");
152
+ return lines.join("\n");
153
+ }
154
+ const keys = Object.keys(eff.sections);
155
+ const hasContent = eff.apps.length > 0 ||
156
+ keys.some((k) => eff.sections[k].all || eff.sections[k].items.length > 0);
157
+ if (!hasContent) {
158
+ lines.push(" (no grants — org defaults only)");
159
+ return lines.join("\n");
160
+ }
161
+ for (const key of keys) {
162
+ const s = eff.sections[key];
163
+ if (s.all) {
164
+ lines.push(` ${key}: all${s.via ? ` (via ${s.via})` : ""}`);
165
+ }
166
+ else if (s.items.length > 0) {
167
+ // Skip empty sections — the server sends every section key even when the
168
+ // member has no grant there; printing "key: -" for each is just noise.
169
+ const items = s.items.map((i) => `${i.resource_id} (${i.via})`).join(", ");
170
+ lines.push(` ${key}: ${items}`);
171
+ }
172
+ }
173
+ if (eff.apps.length > 0) {
174
+ lines.push(" apps:");
175
+ for (const a of eff.apps)
176
+ lines.push(` ${a.name} (${a.via})`);
177
+ }
178
+ return lines.join("\n");
179
+ }
180
+ export function appTable(apps) {
181
+ return formatTable(["slug", "name", "status", "access", "your_role", "manage"], apps.map((a) => [
182
+ a.app_slug,
183
+ a.name,
184
+ a.status,
185
+ a.access_mode,
186
+ a.your_role ?? "-",
187
+ a.can_manage ? "yes" : "no",
188
+ ]));
189
+ }
190
+ // Resolve an app reference — a slug (preferred) or a UUID — against a fetched app
191
+ // list.
192
+ export function resolveAppRef(apps, ref) {
193
+ const needle = ref.trim();
194
+ if (!needle)
195
+ throw new OrgAdminError("Expected an app (slug or UUID).");
196
+ const bySlug = apps.find((a) => a.app_slug === needle);
197
+ if (bySlug)
198
+ return bySlug;
199
+ const byUuid = apps.find((a) => a.uuid === needle);
200
+ if (byUuid)
201
+ return byUuid;
202
+ throw new OrgAdminError(`No app matches "${ref}" (try a slug or UUID).`);
203
+ }
204
+ export const ACCESS_MODES = ["organization", "private", "restricted"];
205
+ export function parseAccessMode(value) {
206
+ const v = (value ?? "").trim().toLowerCase();
207
+ if (ACCESS_MODES.includes(v)) {
208
+ return v;
209
+ }
210
+ throw new OrgAdminError(`--mode must be one of: ${ACCESS_MODES.join(", ")}.`);
211
+ }
212
+ export function renderAppAccess(slug, access) {
213
+ const lines = [`Access for ${slug}: ${access.mode}`];
214
+ if (access.grants.length === 0) {
215
+ lines.push(" (no per-user grants)");
216
+ return lines.join("\n");
217
+ }
218
+ lines.push(formatTable(["email", "name", "role"], access.grants.map((g) => [g.email, g.name, g.role]))
219
+ .split("\n")
220
+ .map((l) => ` ${l}`)
221
+ .join("\n"));
222
+ return lines.join("\n");
223
+ }
224
+ // Comma-separated member refs (emails/uuids) for `--members` in restricted mode.
225
+ export function parseMemberList(value) {
226
+ return value === undefined ? [] : splitList(value);
227
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "railcode",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Developer CLI for the multi-tenant Railcode platform: log in, scaffold, and deploy static apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/static/sdk.js CHANGED
@@ -82,6 +82,7 @@
82
82
  localStorage.setItem("__sdk_open", v ? "1" : "0");
83
83
  root.getElementById("panel").style.display = v ? "flex" : "none";
84
84
  applyWidth();
85
+ window.dispatchEvent(new CustomEvent("railcode:inspector", { detail: { open: v } }));
85
86
  }
86
87
  var escMap = { "&": "&amp;", "<": "&lt;", ">": "&gt;" };
87
88
  var escHtml = (s) => String(s == null ? "" : s).replace(/[&<>]/g, (c) => escMap[c]);
@@ -106,7 +107,9 @@
106
107
  if (entries.length > 100) entries.shift();
107
108
  render();
108
109
  },
109
- update: render
110
+ update: render,
111
+ toggle: () => setOpen(!isOpen),
112
+ isOpen: () => isOpen
110
113
  };
111
114
 
112
115
  // ../sdk/src/config.ts
@@ -219,14 +222,84 @@
219
222
  }();
220
223
  }
221
224
 
225
+ // ../sdk/src/identity.ts
226
+ var me = () => track("me", "me()", () => call("GET", "/me"));
227
+ var appUsers = () => track("app-users", "appUsers()", () => call("GET", "/app-users"));
228
+ var roles = () => track("roles", "roles()", () => call("GET", "/roles"));
229
+
230
+ // ../sdk/src/debug-fab.ts
231
+ function mount2() {
232
+ const host2 = document.createElement("div");
233
+ host2.id = "__sdk_debug_fab";
234
+ const root2 = host2.attachShadow({ mode: "open" });
235
+ root2.innerHTML = `
236
+ <style>
237
+ :host { all: initial; }
238
+ button {
239
+ box-sizing: border-box; position: fixed; right: 16px; bottom: 16px;
240
+ width: 36px; height: 36px; border-radius: 50%; display: flex;
241
+ align-items: center; justify-content: center; cursor: pointer; padding: 0;
242
+ background: #14171c; color: #d6d9de; border: 1px solid #2c333d;
243
+ box-shadow: 0 2px 10px rgba(0,0,0,.35); z-index: 2147483001;
244
+ }
245
+ button:hover { background: #1c2128; color: #fff; }
246
+ svg { width: 16px; height: 16px; }
247
+ </style>
248
+ <button title="SDK activity (Ctrl+\`)" aria-label="Toggle SDK activity">
249
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
250
+ stroke-linecap="round" stroke-linejoin="round">
251
+ <path d="M12 19h8"></path>
252
+ <path d="m4 17 6-6-6-6"></path>
253
+ </svg>
254
+ </button>`;
255
+ root2.querySelector("button").onclick = () => Inspector.toggle();
256
+ (document.body || document.documentElement).appendChild(host2);
257
+ const sync = () => {
258
+ host2.style.display = Inspector.isOpen() ? "none" : "";
259
+ };
260
+ sync();
261
+ window.addEventListener("railcode:inspector", sync);
262
+ }
263
+ me().then((who) => {
264
+ if (who.user.is_admin) mount2();
265
+ }).catch(() => {
266
+ });
267
+
222
268
  // ../sdk/src/agents.ts
223
- async function doInvoke(name, input) {
269
+ var TERMINAL = ["success", "failed", "cancelled", "limit_exceeded"];
270
+ var POLL_MIN_MS = 400;
271
+ var POLL_MAX_MS = 2e3;
272
+ var POLL_BACKOFF = 1.4;
273
+ function isFinished(run) {
274
+ return TERMINAL.includes(run.status);
275
+ }
276
+ async function doStart(name, input) {
224
277
  return call("POST", `/agents/${encodeURIComponent(name)}`, { body: { input } });
225
278
  }
279
+ async function doGet(requestId) {
280
+ return call("GET", `/agents/runs/${encodeURIComponent(requestId)}`);
281
+ }
282
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
283
+ async function doInvoke(name, input) {
284
+ let run = await doStart(name, input);
285
+ let wait = POLL_MIN_MS;
286
+ while (!isFinished(run)) {
287
+ await sleep(wait);
288
+ wait = Math.min(wait * POLL_BACKOFF, POLL_MAX_MS);
289
+ run = await doGet(run.request_id);
290
+ }
291
+ return run;
292
+ }
293
+ function start(name, input) {
294
+ return track("agents", `agents.start(${q(name)})`, () => doStart(name, input));
295
+ }
296
+ function get(requestId) {
297
+ return doGet(requestId);
298
+ }
226
299
  function invoke(name, input) {
227
300
  return track("agents", `agents.invoke(${q(name)})`, () => doInvoke(name, input));
228
301
  }
229
- var agents = { invoke };
302
+ var agents = { invoke, start, get };
230
303
 
231
304
  // ../sdk/src/databases.ts
232
305
  var QUERY_LABEL_MAX = 80;
@@ -567,11 +640,6 @@
567
640
  delete: shared2.delete
568
641
  };
569
642
 
570
- // ../sdk/src/identity.ts
571
- var me = () => track("me", "me()", () => call("GET", "/me"));
572
- var appUsers = () => track("app-users", "appUsers()", () => call("GET", "/app-users"));
573
- var roles = () => track("roles", "roles()", () => call("GET", "/roles"));
574
-
575
643
  // ../sdk/src/llm.ts
576
644
  function label2(method, input, opts = {}) {
577
645
  const preview = typeof input === "string" ? shorten(input.replace(/\s+/g, " ").trim(), 70) : `${input.length} message${input.length === 1 ? "" : "s"}`;