@todoforai/tfa-surface 0.1.1 → 0.1.3
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/dist/cli.js +79 -64
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -37,9 +37,13 @@ function writeSecure(data) {
|
|
|
37
37
|
}
|
|
38
38
|
function readStoredAuth(apiUrl) {
|
|
39
39
|
const c = read();
|
|
40
|
-
const apiToken = c[apiUrl] || c.apiToken || c.apiKey || "";
|
|
41
40
|
const host = c.backendHost || "";
|
|
42
41
|
const storedApiUrl = typeof c.apiUrl === "string" && c.apiUrl ? c.apiUrl : host ? `${host === "localhost" || host === "127.0.0.1" ? "http" : "https"}://${host}` : undefined;
|
|
42
|
+
const dstIsForThisUrl = !storedApiUrl || normalizeApiUrl(storedApiUrl) === normalizeApiUrl(apiUrl);
|
|
43
|
+
const isDst = typeof c.apiToken === "string" && c.apiToken.startsWith("dst_");
|
|
44
|
+
const dst = dstIsForThisUrl && isDst ? c.apiToken : "";
|
|
45
|
+
const fallbackApiToken = isDst && !dstIsForThisUrl ? "" : c.apiToken;
|
|
46
|
+
const apiToken = dst || c[apiUrl] || fallbackApiToken || c.apiKey || "";
|
|
43
47
|
return {
|
|
44
48
|
apiToken: typeof apiToken === "string" ? apiToken : "",
|
|
45
49
|
apiUrl: storedApiUrl,
|
|
@@ -63,6 +67,8 @@ async function refreshDeviceToken(opts = {}) {
|
|
|
63
67
|
const apiUrl = normalizeApiUrl(opts.apiUrl || envUrl || stored.apiUrl || DEFAULT_API_URL);
|
|
64
68
|
if (!stored.deviceId || !stored.deviceSecret)
|
|
65
69
|
return "";
|
|
70
|
+
if (stored.apiUrl && normalizeApiUrl(stored.apiUrl) !== apiUrl)
|
|
71
|
+
return "";
|
|
66
72
|
try {
|
|
67
73
|
const res = await fetch(`${apiUrl}/api/v1/cli/device/token`, {
|
|
68
74
|
method: "POST",
|
|
@@ -89,7 +95,7 @@ async function fetchWithRenew(url, init, opts) {
|
|
|
89
95
|
return fetch(url, { ...init, headers });
|
|
90
96
|
};
|
|
91
97
|
const res = await send(opts.token);
|
|
92
|
-
if (renewOn.includes(res.status)
|
|
98
|
+
if (renewOn.includes(res.status)) {
|
|
93
99
|
const fresh = await refreshDeviceToken({ apiUrl: opts.apiUrl });
|
|
94
100
|
if (fresh && fresh !== opts.token)
|
|
95
101
|
return { res: await send(fresh), token: fresh };
|
|
@@ -110,7 +116,7 @@ function resolveToken(opts = {}) {
|
|
|
110
116
|
// package.json
|
|
111
117
|
var package_default = {
|
|
112
118
|
name: "@todoforai/tfa-surface",
|
|
113
|
-
version: "0.1.
|
|
119
|
+
version: "0.1.3",
|
|
114
120
|
description: "TODOforAI surface CLI — see and drive what is shown in the user's live UI (describe, todo/panel views, renderer cards, surface render, controls). Replaces tfa-frontend. Reuses bridge credentials.",
|
|
115
121
|
type: "module",
|
|
116
122
|
bin: {
|
|
@@ -150,22 +156,23 @@ Usage:
|
|
|
150
156
|
tfa-surface <command> [args...]
|
|
151
157
|
|
|
152
158
|
Discovery:
|
|
153
|
-
describe
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
for the raw structure.
|
|
158
|
-
|
|
159
|
-
Todos (views):
|
|
160
|
-
todo open <todoId> [--popout] Open a todo (full-page overlay); --popout
|
|
161
|
-
forces a floating preview window.
|
|
162
|
-
todo focus <todoId> Focus an open todo's window.
|
|
163
|
-
todo close <todoId> Close an open todo view.
|
|
159
|
+
describe Every surface in the target frontend — kind, id,
|
|
160
|
+
ref, focus; plus sidebar, embedded-browser CDP
|
|
161
|
+
URL and the show instances loaded in each todo.
|
|
162
|
+
YAML; --json for the raw structure.
|
|
164
163
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
panel
|
|
164
|
+
Surfaces — a ref is a todo id, <todoId>:<alias> (a shown artifact, live) or an http(s) url:
|
|
165
|
+
open overlay <todoId> The full-page todo (todos only).
|
|
166
|
+
open window <ref> A floating preview window (any ref).
|
|
167
|
+
open panel <view> [--todo <id>] [--url <url>]
|
|
168
|
+
A todo's side panel: changes|agents|preview|browser|terminal
|
|
169
|
+
(--todo defaults to $TODOFORAI_TODO_ID).
|
|
170
|
+
open card <ref> [--id <id>] [--title <t>] [--group <slug>]
|
|
171
|
+
A card on the project board, for every member —
|
|
172
|
+
its own card, or a tile inside a group's card.
|
|
173
|
+
Persisted; the same --id updates in place.
|
|
174
|
+
close <kind> <id> overlay: no id · window: the ref · panel: the todo · card: its id
|
|
175
|
+
focus window <ref> Bring a window (or its docked tab) to front.
|
|
169
176
|
|
|
170
177
|
Renderer cards (data plane — works headless, no frontend needed;
|
|
171
178
|
--project defaults to $TODOFORAI_PROJECT_ID):
|
|
@@ -187,6 +194,7 @@ Content (inherited from tfa-frontend, broadcast to all tabs):
|
|
|
187
194
|
|
|
188
195
|
Options:
|
|
189
196
|
--frontend <tabId> Target frontend (overrides $TODOFORAI_FRONTEND_ID)
|
|
197
|
+
--id <surfaceId> Card id (open card); --group <slug> files it into a group card
|
|
190
198
|
--api-url <url> Backend URL (default: env/credentials)
|
|
191
199
|
--api-token <tok> Token (default: env/credentials)
|
|
192
200
|
--json Machine-readable output
|
|
@@ -196,9 +204,12 @@ Options:
|
|
|
196
204
|
Examples:
|
|
197
205
|
tfa-surface describe
|
|
198
206
|
tfa-surface card add kpi-tile dashboards/kpi.html --title "KPI tile"
|
|
199
|
-
tfa-surface
|
|
200
|
-
tfa-surface
|
|
201
|
-
|
|
207
|
+
tfa-surface open window 3f2a91...
|
|
208
|
+
tfa-surface open window $TODOFORAI_TODO_ID:kpi # a shown artifact, in its own window
|
|
209
|
+
tfa-surface open card $TODOFORAI_TODO_ID:kpi --title "Weekly KPIs" --group marketing
|
|
210
|
+
tfa-surface open panel browser --url https://example.com
|
|
211
|
+
tfa-surface close card weekly-kpis
|
|
212
|
+
agent-browser connect "$(tfa-surface describe --json | jq -r '.surfaces[] | select(.browser) | .browser.cdp' | head -1)"
|
|
202
213
|
git diff --stat | tfa-surface show chat.panel $TODOFORAI_TODO_ID
|
|
203
214
|
|
|
204
215
|
Exit codes: 0 ok · 1 error · 2 frontend not reachable / no frontend id`);
|
|
@@ -278,24 +289,18 @@ function printResult(ctx, data) {
|
|
|
278
289
|
function printDescribe(d) {
|
|
279
290
|
console.log(`frontend: ${d.frontendId} (${d.platform}, ${d.kind})`);
|
|
280
291
|
console.log(`sidebar: ${d.sidebar}`);
|
|
281
|
-
console.log(d.
|
|
282
|
-
for (const
|
|
283
|
-
const flags = [
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (t.shows?.length) {
|
|
292
|
+
console.log(d.surfaces.length ? "surfaces:" : "surfaces: []");
|
|
293
|
+
for (const s of d.surfaces) {
|
|
294
|
+
const flags = [s.focused ? "focused" : "", s.docked ? "docked" : "", s.view ?? "", s.group ? `in ${s.group}` : ""].filter(Boolean).join(", ");
|
|
295
|
+
const id = s.kind === "overlay" || s.id === s.ref ? "" : ` ${s.id}`;
|
|
296
|
+
console.log(` ${s.kind}${id}: ${s.ref}${flags ? ` ${flags}` : ""}${s.title ? ` # ${s.title}` : ""}`);
|
|
297
|
+
if (s.browser)
|
|
298
|
+
console.log(` browser: ${s.browser.open ? s.browser.url : "(idle)"} cdp: ${s.browser.cdp}`);
|
|
299
|
+
if (s.shows?.length) {
|
|
290
300
|
console.log(" shows:");
|
|
291
|
-
for (const
|
|
292
|
-
const bits = [
|
|
293
|
-
|
|
294
|
-
s.mime || "",
|
|
295
|
-
s.url ? `url ${s.url}` : "",
|
|
296
|
-
s.title || ""
|
|
297
|
-
].filter(Boolean);
|
|
298
|
-
console.log(` ${s.ref}${bits.length ? ` ${bits.join(" ")}` : ""}`);
|
|
301
|
+
for (const x of s.shows) {
|
|
302
|
+
const bits = [x.card ? `card=${x.card}` : "", x.mime || "", x.url ? `url ${x.url}` : "", x.title || ""].filter(Boolean);
|
|
303
|
+
console.log(` ${x.ref}${bits.length ? ` ${bits.join(" ")}` : ""}`);
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
306
|
}
|
|
@@ -309,7 +314,8 @@ async function main() {
|
|
|
309
314
|
project: { type: "string" },
|
|
310
315
|
title: { type: "string" },
|
|
311
316
|
description: { type: "string" },
|
|
312
|
-
|
|
317
|
+
id: { type: "string" },
|
|
318
|
+
group: { type: "string" },
|
|
313
319
|
"api-url": { type: "string" },
|
|
314
320
|
"api-token": { type: "string" },
|
|
315
321
|
json: { type: "boolean", default: false },
|
|
@@ -348,37 +354,46 @@ async function main() {
|
|
|
348
354
|
printDescribe(result);
|
|
349
355
|
break;
|
|
350
356
|
}
|
|
351
|
-
case "
|
|
352
|
-
const [
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
case "panel": {
|
|
367
|
-
const [verb, surface] = args;
|
|
368
|
-
if (verb === "open") {
|
|
369
|
-
if (!surface)
|
|
370
|
-
die("usage: tfa-surface panel open changes|preview|browser|terminal [--todo <id>] [--url <url>]");
|
|
371
|
-
const result = await uiCall(ctx, "panel.open", { todoId: todoArg(), surface, ...values.url ? { url: values.url } : {} });
|
|
357
|
+
case "open": {
|
|
358
|
+
const [kind, arg] = args;
|
|
359
|
+
const usage = "usage: tfa-surface open overlay <todoId> | window <ref> | panel <view> [--todo <id>] [--url <url>] | card <ref> [--id <id>] [--title <t>] [--group <slug>]";
|
|
360
|
+
if (kind === "overlay" || kind === "window") {
|
|
361
|
+
if (!arg)
|
|
362
|
+
die(usage);
|
|
363
|
+
const result = await uiCall(ctx, "surface.open", { kind, ref: arg });
|
|
364
|
+
console.log(ctx.json ? JSON.stringify(result) : `${result.created ? "opened" : "already open"} (${kind})`);
|
|
365
|
+
} else if (kind === "panel") {
|
|
366
|
+
if (!arg)
|
|
367
|
+
die(usage);
|
|
368
|
+
const result = await uiCall(ctx, "surface.open", { kind, id: todoArg(), view: arg, ref: values.url ?? arg });
|
|
372
369
|
const pending = result?.browserReady === false;
|
|
373
370
|
console.log(ctx.json ? JSON.stringify(result ?? { ok: true }) : pending ? "ok (browser opening — its CDP url appears in `device_list` shortly)" : "ok");
|
|
374
|
-
} else if (
|
|
375
|
-
|
|
376
|
-
|
|
371
|
+
} else if (kind === "card") {
|
|
372
|
+
if (!arg)
|
|
373
|
+
die(usage);
|
|
374
|
+
const result = await uiCall(ctx, "surface.open", {
|
|
375
|
+
kind,
|
|
376
|
+
ref: arg,
|
|
377
|
+
...values.id ? { id: values.id } : {},
|
|
378
|
+
...values.title !== undefined ? { title: values.title } : {},
|
|
379
|
+
...values.group !== undefined ? { group: values.group } : {}
|
|
380
|
+
});
|
|
381
|
+
console.log(ctx.json ? JSON.stringify(result) : `card ${result.id} ${result.created ? "added to" : "updated on"} the board`);
|
|
377
382
|
} else {
|
|
378
|
-
die(
|
|
383
|
+
die(usage);
|
|
379
384
|
}
|
|
380
385
|
break;
|
|
381
386
|
}
|
|
387
|
+
case "close":
|
|
388
|
+
case "focus": {
|
|
389
|
+
const [kind, arg] = args;
|
|
390
|
+
const id = kind === "overlay" ? "" : kind === "panel" ? arg || todoArg() : arg;
|
|
391
|
+
if (!kind || kind !== "overlay" && !id)
|
|
392
|
+
die(`usage: tfa-surface ${cmd} overlay | window <ref> | panel [<todoId>] | card <id>`);
|
|
393
|
+
await uiCall(ctx, `surface.${cmd}`, { kind, id });
|
|
394
|
+
console.log(ctx.json ? '{"ok":true}' : "ok");
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
382
397
|
case "card": {
|
|
383
398
|
const [verb, name] = args;
|
|
384
399
|
const cardPath = (n) => `/resources/cards/${projectArg()}/${encodeURIComponent(n)}`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@todoforai/tfa-surface",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "TODOforAI surface CLI
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "TODOforAI surface CLI — see and drive what is shown in the user's live UI (describe, todo/panel views, renderer cards, surface render, controls). Replaces tfa-frontend. Reuses bridge credentials.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tfa-surface": "dist/cli.js"
|