@enter-pro/enter-cli 0.4.1 → 0.4.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/README.md +167 -0
- package/dist/auth.d.ts +4 -0
- package/dist/auth.js +132 -6
- package/dist/client.d.ts +4 -6
- package/dist/client.js +97 -93
- package/dist/commands/config.js +5 -10
- package/dist/commands/domain.js +3 -6
- package/dist/commands/login.js +18 -12
- package/dist/commands/logout.js +7 -3
- package/dist/commands/project.js +85 -70
- package/dist/commands/thread-tasks.d.ts +2 -0
- package/dist/commands/thread-tasks.js +23 -0
- package/dist/commands/thread.d.ts +54 -0
- package/dist/commands/thread.js +547 -209
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/workspace.js +7 -10
- package/dist/config.d.ts +0 -1
- package/dist/config.js +10 -4
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +29 -0
- package/dist/index.js +9 -2
- package/dist/output.d.ts +0 -16
- package/dist/output.js +0 -18
- package/dist/poll.d.ts +1 -0
- package/dist/poll.js +3 -1
- package/dist/safe-output.d.ts +6 -0
- package/dist/safe-output.js +29 -0
- package/dist/thread-events.d.ts +37 -0
- package/dist/thread-events.js +196 -0
- package/dist/workflow.d.ts +44 -0
- package/dist/workflow.js +34 -0
- package/package.json +22 -10
- package/scripts/install-hosts.mjs +29 -0
- package/skills/enter/SKILL.md +36 -0
- package/skills/enter/references/configuration.md +19 -0
package/dist/commands/login.js
CHANGED
|
@@ -2,12 +2,12 @@ import { Command } from "commander";
|
|
|
2
2
|
import { createServer } from "http";
|
|
3
3
|
import { randomBytes, createHash } from "crypto";
|
|
4
4
|
import { execSync } from "child_process";
|
|
5
|
-
import { saveCredentials } from "../auth.js";
|
|
6
|
-
import {
|
|
7
|
-
const AUTH0_DOMAIN = "auth.
|
|
8
|
-
const CLIENT_ID =
|
|
5
|
+
import { saveCredentials, verifyAccessToken, OAUTH_TOKEN_URL, OAUTH_CLIENT_ID } from "../auth.js";
|
|
6
|
+
import { printResult, getFormat } from "../output.js";
|
|
7
|
+
const AUTH0_DOMAIN = "auth.converge.ai";
|
|
8
|
+
const CLIENT_ID = OAUTH_CLIENT_ID;
|
|
9
9
|
const AUTHORIZE_URL = `https://${AUTH0_DOMAIN}/authorize`;
|
|
10
|
-
const TOKEN_URL =
|
|
10
|
+
const TOKEN_URL = OAUTH_TOKEN_URL;
|
|
11
11
|
const AUDIENCE = "https://api.enter.pro";
|
|
12
12
|
const SCOPE = "openid profile email offline_access";
|
|
13
13
|
const REDIRECT_URI = "http://localhost:19820";
|
|
@@ -83,6 +83,7 @@ function startOAuthFlow() {
|
|
|
83
83
|
}
|
|
84
84
|
try {
|
|
85
85
|
const tokens = await exchangeCodeForTokens(code, codeVerifier);
|
|
86
|
+
await verifyAccessToken(tokens.access_token);
|
|
86
87
|
const expiresAt = new Date(Date.now() + tokens.expires_in * 1000).toISOString();
|
|
87
88
|
saveCredentials({
|
|
88
89
|
access_token: tokens.access_token,
|
|
@@ -93,7 +94,6 @@ function startOAuthFlow() {
|
|
|
93
94
|
});
|
|
94
95
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
95
96
|
res.end(`<html><body><h2>Login Successful!</h2><p>You can close this window and return to the terminal.</p></body></html>`);
|
|
96
|
-
printMessage("Login successful. Credentials saved.");
|
|
97
97
|
clearTimeout(timeoutId);
|
|
98
98
|
server.close();
|
|
99
99
|
resolve();
|
|
@@ -117,8 +117,8 @@ function startOAuthFlow() {
|
|
|
117
117
|
code_challenge_method: "S256",
|
|
118
118
|
});
|
|
119
119
|
const authUrl = `${AUTHORIZE_URL}?${params}`;
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
console.error("Opening browser for authentication...");
|
|
121
|
+
console.error(`If browser doesn't open, visit:\n${authUrl}\n`);
|
|
122
122
|
openBrowser(authUrl);
|
|
123
123
|
});
|
|
124
124
|
timeoutId = setTimeout(() => {
|
|
@@ -130,14 +130,20 @@ function startOAuthFlow() {
|
|
|
130
130
|
export const loginCmd = new Command("login")
|
|
131
131
|
.description("Authenticate with Enter platform")
|
|
132
132
|
.option("--api-key <key>", "Authenticate with a workspace API key instead of OAuth")
|
|
133
|
-
.action(async (opts) => {
|
|
133
|
+
.action(async (opts, cmd) => {
|
|
134
134
|
if (opts.apiKey) {
|
|
135
|
+
await verifyAccessToken(opts.apiKey);
|
|
135
136
|
saveCredentials({
|
|
136
137
|
access_token: opts.apiKey,
|
|
137
138
|
token_type: "Bearer",
|
|
138
139
|
});
|
|
139
|
-
printMessage("API key saved. You are now authenticated.");
|
|
140
|
-
return;
|
|
141
140
|
}
|
|
142
|
-
|
|
141
|
+
else {
|
|
142
|
+
await startOAuthFlow();
|
|
143
|
+
}
|
|
144
|
+
const injected = Boolean(process.env.ENTER_API_KEY);
|
|
145
|
+
const message = injected
|
|
146
|
+
? "Local credentials verified and saved. ENTER_API_KEY still takes precedence; this has not switched the active account. Change authentication in the host or unset ENTER_API_KEY to use the saved credentials."
|
|
147
|
+
: "Login successful. Credentials saved.";
|
|
148
|
+
printResult(getFormat(cmd), { credentials_saved: true, auth_source: injected ? "environment" : "local", ...(!injected ? { authenticated: true } : {}), message }, message);
|
|
143
149
|
});
|
package/dist/commands/logout.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { clearCredentials } from "../auth.js";
|
|
3
|
-
import {
|
|
3
|
+
import { printResult, getFormat } from "../output.js";
|
|
4
4
|
export const logoutCmd = new Command("logout")
|
|
5
5
|
.description("Clear stored credentials")
|
|
6
|
-
.action(async () => {
|
|
6
|
+
.action(async (_opts, cmd) => {
|
|
7
7
|
clearCredentials();
|
|
8
|
-
|
|
8
|
+
const injected = Boolean(process.env.ENTER_API_KEY);
|
|
9
|
+
const message = injected
|
|
10
|
+
? "Local credentials cleared. ENTER_API_KEY still takes precedence; change or disconnect authentication in the host, or unset it in your shell, to switch accounts."
|
|
11
|
+
: "Stored credentials cleared. Logged out.";
|
|
12
|
+
printResult(getFormat(cmd), { logged_out: !injected, local_credentials_cleared: true, auth_source: injected ? "environment" : "none", message }, message);
|
|
9
13
|
});
|
package/dist/commands/project.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
1
2
|
import { Command } from "commander";
|
|
2
3
|
import { writeFileSync } from "fs";
|
|
3
4
|
import * as client from "../client.js";
|
|
4
5
|
import { print, printMessage, printResult, printTable, pickList, getFormat } from "../output.js";
|
|
5
6
|
import { pollUntil, TimeoutError } from "../poll.js";
|
|
7
|
+
import { reportThread, threadInteraction } from "./thread.js";
|
|
8
|
+
import { safeOutput } from "../safe-output.js";
|
|
9
|
+
import { RequestError } from "../errors.js";
|
|
6
10
|
import { resolveLifecycleStatus } from "../lifecycle.js";
|
|
7
11
|
export const projectCmd = new Command("project")
|
|
8
12
|
.alias("proj")
|
|
@@ -61,7 +65,8 @@ projectCmd
|
|
|
61
65
|
.action(async (id, _opts, cmd) => {
|
|
62
66
|
const data = await client.get(`/v1/projects/${id}/detail`);
|
|
63
67
|
const p = data;
|
|
64
|
-
const
|
|
68
|
+
const project = unwrapProject(p);
|
|
69
|
+
const lifecycle = resolveLifecycleStatus(project);
|
|
65
70
|
const enriched = { ...p, lifecycle_status: lifecycle };
|
|
66
71
|
const format = getFormat(cmd);
|
|
67
72
|
if (format !== "table") {
|
|
@@ -69,14 +74,14 @@ projectCmd
|
|
|
69
74
|
return;
|
|
70
75
|
}
|
|
71
76
|
printTable(["Project ID", "Name", "Visibility", "Status", "Lifecycle", "Preview URL", "Workspace", "Updated"], [[
|
|
72
|
-
String(
|
|
73
|
-
String(
|
|
74
|
-
String(
|
|
75
|
-
String(
|
|
77
|
+
String(project.project_id ?? ""),
|
|
78
|
+
String(project.name ?? ""),
|
|
79
|
+
String(project.visibility ?? ""),
|
|
80
|
+
String(project.status ?? ""),
|
|
76
81
|
lifecycle,
|
|
77
|
-
String(
|
|
78
|
-
String(
|
|
79
|
-
String(
|
|
82
|
+
String(project.preview_url ?? ""),
|
|
83
|
+
String(project.workspace_id ?? ""),
|
|
84
|
+
String(project.updated_at ?? ""),
|
|
80
85
|
]]);
|
|
81
86
|
});
|
|
82
87
|
projectCmd
|
|
@@ -89,6 +94,9 @@ projectCmd
|
|
|
89
94
|
.option("--wait", "Wait until the first build completes")
|
|
90
95
|
.option("--timeout <seconds>", "Timeout for --wait in seconds", "300")
|
|
91
96
|
.action(async (id, opts, cmd) => {
|
|
97
|
+
const seconds = Number(opts.timeout);
|
|
98
|
+
if (opts.wait && (!Number.isFinite(seconds) || seconds <= 0 || seconds * 1000 > 2147483647))
|
|
99
|
+
throw new Error("--timeout must be a positive bounded number");
|
|
92
100
|
const body = {};
|
|
93
101
|
if (opts.name)
|
|
94
102
|
body.name = opts.name;
|
|
@@ -99,38 +107,15 @@ projectCmd
|
|
|
99
107
|
if (opts.planMode)
|
|
100
108
|
body.plan_mode = true;
|
|
101
109
|
const data = await client.post(`/v1/workspaces/${id}/projects`, body);
|
|
102
|
-
|
|
103
|
-
print(getFormat(cmd), data);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const created = data;
|
|
110
|
+
const created = unwrapProject(data);
|
|
107
111
|
const projectId = String(created.project_id ?? created.id ?? "");
|
|
108
|
-
if (!projectId)
|
|
109
|
-
|
|
112
|
+
if (!projectId)
|
|
113
|
+
throw new Error("Creation response is missing project ID; creation outcome is unknown. Inspect project list before retrying.");
|
|
114
|
+
if (!opts.wait) {
|
|
115
|
+
print(getFormat(cmd), safeOutput({ ...data, submission_status: "accepted", ...threadInteraction(projectId) }));
|
|
110
116
|
return;
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
const timeoutMs = parseInt(opts.timeout, 10) * 1000;
|
|
114
|
-
const buildingStatuses = new Set(["initializing", "building"]);
|
|
115
|
-
try {
|
|
116
|
-
const result = await pollUntil(() => client.get(`/v1/projects/${projectId}/detail`), (d) => !buildingStatuses.has(String(d.status ?? "")), {
|
|
117
|
-
intervalMs: 3000,
|
|
118
|
-
timeoutMs,
|
|
119
|
-
onTick: (elapsed) => {
|
|
120
|
-
process.stderr.write(`\rWaiting... ${Math.round(elapsed / 1000)}s elapsed`);
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
process.stderr.write("\n");
|
|
124
|
-
const enriched = { ...result, lifecycle_status: resolveLifecycleStatus(result) };
|
|
125
|
-
print(getFormat(cmd), enriched);
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
if (err instanceof TimeoutError) {
|
|
129
|
-
console.error(`\nTimed out after ${opts.timeout}s. Project may still be building.`);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
throw err;
|
|
133
|
-
}
|
|
118
|
+
await reportThread(projectId, { timeout: String(seconds), requireBuild: true, compact: true }, cmd, true);
|
|
134
119
|
});
|
|
135
120
|
projectCmd
|
|
136
121
|
.command("rename <project_id> <new_name>")
|
|
@@ -150,83 +135,108 @@ projectCmd
|
|
|
150
135
|
.command("download <project_id>")
|
|
151
136
|
.description("Download project as zip file")
|
|
152
137
|
.option("--out <path>", "Output file path")
|
|
153
|
-
.action(async (id, opts) => {
|
|
138
|
+
.action(async (id, opts, cmd) => {
|
|
154
139
|
const data = await client.getRaw(`/v1/projects/${id}/download`);
|
|
155
140
|
const outPath = opts.out || `${id}.zip`;
|
|
156
|
-
writeFileSync(outPath, Buffer.from(
|
|
157
|
-
|
|
141
|
+
writeFileSync(outPath, Buffer.from(data));
|
|
142
|
+
printResult(getFormat(cmd), { path: outPath }, `Project downloaded to ${outPath}`);
|
|
158
143
|
});
|
|
159
144
|
projectCmd
|
|
160
145
|
.command("publish <project_id>")
|
|
161
146
|
.description("Publish a project (synchronous: waits for completion and verifies URL)")
|
|
162
147
|
.option("--timeout <seconds>", "Timeout in seconds", "300")
|
|
163
148
|
.action(async (id, opts, cmd) => {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
await client.post(`/v1/projects/${id}/publish`);
|
|
174
|
-
console.error("Waiting for publish to complete...");
|
|
175
|
-
const timeoutMs = parseInt(opts.timeout, 10) * 1000;
|
|
149
|
+
const seconds = Number(opts.timeout);
|
|
150
|
+
if (!Number.isFinite(seconds) || seconds <= 0 || seconds * 1000 > 2147483647)
|
|
151
|
+
throw new Error("--timeout must be a positive bounded number");
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
const timer = setTimeout(() => controller.abort(), seconds * 1000);
|
|
154
|
+
let submitted = false;
|
|
155
|
+
let published = false;
|
|
176
156
|
try {
|
|
177
|
-
const
|
|
157
|
+
const initialDetail = await client.get(`/v1/projects/${id}/detail`, undefined, controller.signal);
|
|
158
|
+
const initialProject = unwrapProject(initialDetail);
|
|
159
|
+
const buildStatus = initialProject.build_status;
|
|
160
|
+
const commitId = String(buildStatus?.commit_id ?? initialProject.commit ?? "");
|
|
161
|
+
if (!commitId) {
|
|
162
|
+
throw new Error("Project has no committed build yet. Wait for the latest turn to finish, then retry.");
|
|
163
|
+
}
|
|
164
|
+
const format = getFormat(cmd);
|
|
165
|
+
const human = format === "table";
|
|
166
|
+
controller.signal.throwIfAborted();
|
|
167
|
+
submitted = true;
|
|
168
|
+
await client.post(`/v1/projects/${id}/publish`, undefined, controller.signal);
|
|
169
|
+
if (human)
|
|
170
|
+
console.error("Waiting for publish to complete...");
|
|
171
|
+
const timeoutMs = seconds * 1000;
|
|
172
|
+
const result = await pollUntil(() => client.get(`/v1/projects/${id}/detail`, undefined, controller.signal), (d) => {
|
|
178
173
|
const view = readPublishStatus(unwrapProject(d));
|
|
179
174
|
// Done when the latest committed build has been published and nothing is queued.
|
|
180
175
|
return view.lastPublishedCommit === commitId && view.unpublishedChanges === 0;
|
|
181
176
|
}, {
|
|
177
|
+
signal: controller.signal,
|
|
182
178
|
intervalMs: 3000,
|
|
183
179
|
timeoutMs,
|
|
184
180
|
onTick: (elapsed) => {
|
|
185
|
-
|
|
181
|
+
if (human)
|
|
182
|
+
process.stderr.write(`\rWaiting... ${Math.round(elapsed / 1000)}s elapsed`);
|
|
186
183
|
},
|
|
187
184
|
});
|
|
188
|
-
|
|
185
|
+
if (human)
|
|
186
|
+
process.stderr.write("\n");
|
|
187
|
+
published = true;
|
|
189
188
|
const project = unwrapProject(result);
|
|
190
189
|
const view = readPublishStatus(project);
|
|
191
190
|
const publishUrl = String(project.publish_url ?? "");
|
|
192
|
-
const reachable = publishUrl ? await verifyUrlReachable(publishUrl) : false;
|
|
193
|
-
if (publishUrl && !reachable) {
|
|
191
|
+
const reachable = publishUrl ? await verifyUrlReachable(publishUrl, 3, 2000, controller.signal) : false;
|
|
192
|
+
if (human && publishUrl && !reachable) {
|
|
194
193
|
console.error(`Warning: ${publishUrl} did not return 200 within retries. The publish completed server-side but the URL may still be propagating.`);
|
|
195
194
|
}
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
if (human)
|
|
196
|
+
printMessage(`Published commit ${commitId.slice(0, 7)} at ${view.lastPublishedAt}. URL: ${publishUrl}${reachable ? " (200 OK)" : ""}`);
|
|
197
|
+
print(format, { ...result, lifecycle_status: resolveLifecycleStatus(project), publish_reachable: reachable });
|
|
198
198
|
}
|
|
199
199
|
catch (err) {
|
|
200
|
-
if (err instanceof TimeoutError) {
|
|
201
|
-
|
|
202
|
-
console.error(`Run "proj urls ${id}" to check current status.`);
|
|
203
|
-
process.exit(1);
|
|
200
|
+
if (err instanceof TimeoutError || controller.signal.aborted) {
|
|
201
|
+
throw new RequestError("PUBLISH_TIMEOUT", `Publish observation exceeded its deadline. Check enter-cli project publish-status ${id} before retrying.`, false, submitted && !published);
|
|
204
202
|
}
|
|
205
203
|
throw err;
|
|
206
204
|
}
|
|
205
|
+
finally {
|
|
206
|
+
clearTimeout(timer);
|
|
207
|
+
}
|
|
207
208
|
});
|
|
208
209
|
// HEAD-first URL probe with a small wall-clock budget. Falls back to GET when
|
|
209
210
|
// HEAD isn't allowed (some hosts return 405). Used by `proj publish` (after
|
|
210
211
|
// publish completes) and `proj urls` (to determine the recommended URL).
|
|
211
|
-
async function verifyUrlReachable(url, attempts = 3, perAttemptMs = 2000) {
|
|
212
|
+
async function verifyUrlReachable(url, attempts = 3, perAttemptMs = 2000, signal) {
|
|
212
213
|
for (let i = 0; i < attempts; i++) {
|
|
214
|
+
signal?.throwIfAborted();
|
|
213
215
|
const ctrl = new AbortController();
|
|
216
|
+
const abort = () => ctrl.abort();
|
|
217
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
214
218
|
const t = setTimeout(() => ctrl.abort(), perAttemptMs);
|
|
215
219
|
try {
|
|
216
220
|
let res = await fetch(url, { method: "HEAD", signal: ctrl.signal });
|
|
217
221
|
// Some hosts reject HEAD (405 / 501) — retry once with GET on this attempt.
|
|
218
222
|
if (res.status === 405 || res.status === 501) {
|
|
223
|
+
await res.body?.cancel();
|
|
219
224
|
res = await fetch(url, { method: "GET", signal: ctrl.signal });
|
|
220
225
|
}
|
|
221
|
-
|
|
226
|
+
await res.body?.cancel();
|
|
222
227
|
if (res.status === 200)
|
|
223
228
|
return true;
|
|
224
229
|
}
|
|
225
230
|
catch {
|
|
231
|
+
signal?.throwIfAborted();
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
ctrl.abort();
|
|
226
235
|
clearTimeout(t);
|
|
236
|
+
signal?.removeEventListener("abort", abort);
|
|
227
237
|
}
|
|
228
238
|
if (i < attempts - 1)
|
|
229
|
-
await
|
|
239
|
+
await delay(1000, undefined, { signal });
|
|
230
240
|
}
|
|
231
241
|
return false;
|
|
232
242
|
}
|
|
@@ -294,8 +304,13 @@ projectCmd
|
|
|
294
304
|
.option("--workspace-id <id>", "Target workspace ID for remix")
|
|
295
305
|
.action(async (id, opts, cmd) => {
|
|
296
306
|
const body = {};
|
|
297
|
-
if (opts.workspaceId)
|
|
298
|
-
|
|
307
|
+
if (opts.workspaceId !== undefined) {
|
|
308
|
+
const workspaceId = Number(opts.workspaceId);
|
|
309
|
+
if (!/^\d+$/.test(opts.workspaceId) || !Number.isSafeInteger(workspaceId) || workspaceId <= 0) {
|
|
310
|
+
throw new Error("--workspace-id must be a positive safe integer");
|
|
311
|
+
}
|
|
312
|
+
body.workspace_id = workspaceId;
|
|
313
|
+
}
|
|
299
314
|
const data = await client.post(`/v1/projects/${id}/remix`, body);
|
|
300
315
|
print(getFormat(cmd), data);
|
|
301
316
|
});
|
|
@@ -411,9 +426,9 @@ mcpCmd
|
|
|
411
426
|
mcpCmd
|
|
412
427
|
.command("delete <project_id> <server_id>")
|
|
413
428
|
.description("Delete an MCP server")
|
|
414
|
-
.action(async (id, serverId) => {
|
|
429
|
+
.action(async (id, serverId, _opts, cmd) => {
|
|
415
430
|
await client.del(`/v1/projects/${id}/mcp/servers/${serverId}`);
|
|
416
|
-
|
|
431
|
+
printResult(getFormat(cmd), { deleted: true, server_id: serverId }, `MCP server ${serverId} deleted.`);
|
|
417
432
|
});
|
|
418
433
|
projectCmd.addCommand(mcpCmd);
|
|
419
434
|
// Project skills subcommand group
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as client from "../client.js";
|
|
2
|
+
import { getFormat, print, printTable } from "../output.js";
|
|
3
|
+
// Queue payloads can contain prompts, attachments and credentials. Expose only
|
|
4
|
+
// the metadata needed to diagnose accepted tasks that have no turn yet.
|
|
5
|
+
export function registerThreadTasks(parent) {
|
|
6
|
+
parent.command("tasks <project_id>")
|
|
7
|
+
.description("List queued/running tasks without exposing prompts or credentials")
|
|
8
|
+
.option("--chat-id <id>", "Scope the queue lookup to a chat")
|
|
9
|
+
.action(async (projectId, opts, cmd) => {
|
|
10
|
+
const data = await client.get(`/v1/projects/${projectId}/thread/tasks`, opts.chatId ? { chat_id: opts.chatId } : undefined);
|
|
11
|
+
if (!Array.isArray(data?.tasks))
|
|
12
|
+
throw new Error("Invalid task queue response: expected tasks array");
|
|
13
|
+
const tasks = data.tasks.map(task => Object.fromEntries(["id", "task_type", "task_status", "created_at", "project_id", "chat_id"]
|
|
14
|
+
.filter(key => typeof task[key] === "string")
|
|
15
|
+
.map(key => [key, task[key]])));
|
|
16
|
+
if (getFormat(cmd) === "table") {
|
|
17
|
+
printTable(["ID", "Type", "Status", "Created", "Chat"], tasks.map(task => ["id", "task_type", "task_status", "created_at", "chat_id"].map(key => String(task[key] ?? ""))));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
print(getFormat(cmd), { project_id: projectId, ...(opts.chatId ? { chat_id: opts.chatId } : {}), tasks, total: tasks.length });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -1,2 +1,56 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
export declare const threadCmd: Command;
|
|
3
|
+
type InteractionOptions = {
|
|
4
|
+
taskId?: string;
|
|
5
|
+
chatId?: string;
|
|
6
|
+
turn?: number;
|
|
7
|
+
requireBuild?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function threadInteraction(id: string, { taskId, chatId, turn, requireBuild }?: InteractionOptions): {
|
|
10
|
+
workflow: {
|
|
11
|
+
actions?: {
|
|
12
|
+
id: unknown;
|
|
13
|
+
kind: unknown;
|
|
14
|
+
decision: string;
|
|
15
|
+
required_fields: {};
|
|
16
|
+
submit_command: unknown;
|
|
17
|
+
}[] | undefined;
|
|
18
|
+
build?: {
|
|
19
|
+
commit: {} | null;
|
|
20
|
+
matches_task: boolean;
|
|
21
|
+
success: {} | null;
|
|
22
|
+
} | undefined;
|
|
23
|
+
observation: {
|
|
24
|
+
timed_out: boolean;
|
|
25
|
+
retrying: boolean;
|
|
26
|
+
interrupted: boolean;
|
|
27
|
+
};
|
|
28
|
+
reason?: string | undefined;
|
|
29
|
+
state: string;
|
|
30
|
+
task: {
|
|
31
|
+
id: {} | null;
|
|
32
|
+
turn: {} | null;
|
|
33
|
+
chat_id: {} | null;
|
|
34
|
+
};
|
|
35
|
+
next_action: string;
|
|
36
|
+
};
|
|
37
|
+
monitoring_required: boolean;
|
|
38
|
+
wait_command: string;
|
|
39
|
+
watch_command: string;
|
|
40
|
+
status_command: string;
|
|
41
|
+
follow_up_command: string;
|
|
42
|
+
instructions: string;
|
|
43
|
+
};
|
|
44
|
+
type MonitorOptions = {
|
|
45
|
+
turn?: string;
|
|
46
|
+
taskId?: string;
|
|
47
|
+
chatId?: string;
|
|
48
|
+
cursor?: string;
|
|
49
|
+
timeout: string;
|
|
50
|
+
transport?: string;
|
|
51
|
+
full?: boolean;
|
|
52
|
+
compact?: boolean;
|
|
53
|
+
requireBuild?: boolean;
|
|
54
|
+
};
|
|
55
|
+
export declare function reportThread(id: string, opts: MonitorOptions, cmd: Command, wait: boolean, watch?: boolean): Promise<void>;
|
|
56
|
+
export {};
|