@geostack/arc 0.1.0
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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +890 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +632 -0
- package/dist/index.js +1514 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
7
|
+
import { ArcAgentClient, ArcDeveloperClient, ArcHttpError, createActionSyncPayload, createArcAgentRuntime, createSlug } from "./index.js";
|
|
8
|
+
const defaultApiUrl = "http://127.0.0.1:4000";
|
|
9
|
+
const defaultDevEmail = "dev@example.test";
|
|
10
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
11
|
+
export async function main(argv = process.argv.slice(2), env = process.env, io = process) {
|
|
12
|
+
const [scope, action, ...rest] = argv;
|
|
13
|
+
try {
|
|
14
|
+
if (!scope || scope === "help" || scope === "--help" || scope === "-h") {
|
|
15
|
+
printHelp(io);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (scope === "config" && action === "set") {
|
|
19
|
+
await configSet(parseArgs(rest), env, io);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
if (scope === "config" && action === "get") {
|
|
23
|
+
await configGet(env, io);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
if (scope === "app" && action === "create") {
|
|
27
|
+
await appCreate(parseArgs(rest), env, io);
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
if (scope === "actions" && action === "sync") {
|
|
31
|
+
await actionsSync(parseArgs(rest), env, io);
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (scope === "agent" && action === "dev-token") {
|
|
35
|
+
await agentDevToken(parseArgs(rest), env, io);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
if (scope === "agent" && action === "whoami") {
|
|
39
|
+
await agentWhoami(parseArgs(rest), env, io);
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
if (scope === "agent" && action === "apps") {
|
|
43
|
+
await agentApps(parseArgs(rest), env, io);
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
if (scope === "agent" && action === "actions") {
|
|
47
|
+
await agentActions(parseArgs(rest), env, io);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
if (scope === "agent" && action === "invoke") {
|
|
51
|
+
await agentInvoke(parseArgs(rest), env, io);
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
if (scope === "invoke") {
|
|
55
|
+
await invokeAction(parseArgs([action ?? "", ...rest].filter(Boolean)), env, io);
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if (scope === "delegations" && action === "list") {
|
|
59
|
+
await delegationsList(parseArgs(rest), env, io);
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
if (scope === "delegations" && action === "show") {
|
|
63
|
+
await delegationsShow(parseArgs(rest), env, io);
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
if (scope === "delegations" && action === "create") {
|
|
67
|
+
await delegationsCreate(parseArgs(rest), env, io);
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
if (scope === "delegations" && action === "revoke") {
|
|
71
|
+
await delegationsRevoke(parseArgs(rest), env, io);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
if (scope === "orgs" && action === "list") {
|
|
75
|
+
await orgsList(parseArgs(rest), env, io);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
if (scope === "orgs" && action === "current") {
|
|
79
|
+
await orgsCurrent(parseArgs(rest), env, io);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
if (scope === "orgs" && action === "members") {
|
|
83
|
+
await orgsMembers(parseArgs(rest), env, io);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
if (scope === "orgs" && action === "invite") {
|
|
87
|
+
await orgsInvite(parseArgs(rest), env, io);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
if (scope === "approvals" && action === "list") {
|
|
91
|
+
await approvalsList(parseArgs(rest), env, io);
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
if (scope === "approvals" && action === "approve") {
|
|
95
|
+
await approvalsResolve("approve", parseArgs(rest), env, io);
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
if (scope === "approvals" && action === "deny") {
|
|
99
|
+
await approvalsResolve("deny", parseArgs(rest), env, io);
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
if (scope === "audit" && action === "tail") {
|
|
103
|
+
await auditTail(parseArgs(rest), env, io);
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
if (scope === "audit" && action === "export") {
|
|
107
|
+
await auditExport(parseArgs(rest), env, io);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
if (scope === "audit" && action === "verify") {
|
|
111
|
+
await auditVerify(parseArgs(rest), env, io);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
if (scope === "audit" && action === "timeline") {
|
|
115
|
+
await auditTimeline(parseArgs(rest), env, io);
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (scope === "dev" && action === "smoke") {
|
|
119
|
+
await devSmoke(parseArgs(rest), env, io);
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`Unknown arc command: ${argv.join(" ")}`);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
printError(error, io);
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function configSet(args, env, io) {
|
|
130
|
+
const store = await loadConfig(env);
|
|
131
|
+
const apiUrl = optionalFlag(args, "api-url");
|
|
132
|
+
const devEmail = optionalFlag(args, "dev-email");
|
|
133
|
+
const orgId = optionalFlag(args, "org");
|
|
134
|
+
if (!apiUrl && !devEmail && !orgId) {
|
|
135
|
+
throw new Error("config set requires --api-url, --dev-email, or --org.");
|
|
136
|
+
}
|
|
137
|
+
if (apiUrl) {
|
|
138
|
+
store.config.apiUrl = trimTrailingSlash(apiUrl);
|
|
139
|
+
}
|
|
140
|
+
if (devEmail) {
|
|
141
|
+
store.config.devEmail = devEmail;
|
|
142
|
+
}
|
|
143
|
+
if (orgId) {
|
|
144
|
+
store.config.currentOrgId = orgId;
|
|
145
|
+
}
|
|
146
|
+
await store.save();
|
|
147
|
+
io.stdout.write(`Arc config saved at ${store.path}\n`);
|
|
148
|
+
}
|
|
149
|
+
async function configGet(env, io) {
|
|
150
|
+
const store = await loadConfig(env);
|
|
151
|
+
io.stdout.write(`${JSON.stringify(sanitizeConfig(store.config), null, 2)}\n`);
|
|
152
|
+
}
|
|
153
|
+
async function appCreate(args, env, io) {
|
|
154
|
+
const name = requiredPositional(args, 0, "app name");
|
|
155
|
+
const executeUrl = requiredFlag(args, "execute-url");
|
|
156
|
+
const slug = optionalFlag(args, "slug") ?? createSlug(name);
|
|
157
|
+
const appUserId = optionalFlag(args, "app-user-id") ?? "local-dev-user";
|
|
158
|
+
const store = await loadConfig(env);
|
|
159
|
+
const client = await ensureDeveloperClient(store, io);
|
|
160
|
+
const response = await client.createApp({
|
|
161
|
+
execute_url: executeUrl,
|
|
162
|
+
name,
|
|
163
|
+
slug
|
|
164
|
+
});
|
|
165
|
+
const app = readRecord(readRecord(response, "data"), "app");
|
|
166
|
+
store.config.currentAppId = readString(app, "id");
|
|
167
|
+
store.config.currentAppSlug = readString(app, "slug");
|
|
168
|
+
try {
|
|
169
|
+
await client.createAppConnection({
|
|
170
|
+
app_id: store.config.currentAppId,
|
|
171
|
+
app_user_id: appUserId
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (!(error instanceof ArcHttpError && error.status === 409)) {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
180
|
+
await store.save();
|
|
181
|
+
io.stdout.write(`Created app ${readString(app, "name")} (${store.config.currentAppSlug})\n` +
|
|
182
|
+
`App id: ${store.config.currentAppId}\n` +
|
|
183
|
+
`Linked local app user: ${appUserId}\n`);
|
|
184
|
+
}
|
|
185
|
+
async function actionsSync(args, env, io) {
|
|
186
|
+
const file = requiredPositional(args, 0, "actions file");
|
|
187
|
+
const actions = await loadActionsFile(file);
|
|
188
|
+
const payload = createActionSyncPayload(actions);
|
|
189
|
+
if (args.flags.has("dry-run")) {
|
|
190
|
+
io.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const store = await loadConfig(env);
|
|
194
|
+
const client = await ensureDeveloperClient(store, io);
|
|
195
|
+
const appId = await resolveAppId(client, store.config, optionalFlag(args, "app"));
|
|
196
|
+
const response = await client.syncActions(appId, payload.actions);
|
|
197
|
+
const synced = readArray(readRecord(response, "data").actions);
|
|
198
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
199
|
+
await store.save();
|
|
200
|
+
io.stdout.write(`Synced ${synced.length} action${synced.length === 1 ? "" : "s"} to app ${appId}\n`);
|
|
201
|
+
}
|
|
202
|
+
async function agentDevToken(args, env, io) {
|
|
203
|
+
const store = await loadConfig(env);
|
|
204
|
+
const client = await ensureDeveloperClient(store, io);
|
|
205
|
+
const response = await client.createDevAgentToken({
|
|
206
|
+
agent_type: optionalFlag(args, "agent-type") ?? "local-cli",
|
|
207
|
+
display_name: optionalFlag(args, "display-name") ?? "Arc CLI"
|
|
208
|
+
});
|
|
209
|
+
const token = readRecord(readRecord(readRecord(response, "data"), "agent_token"), "agent_connection");
|
|
210
|
+
const tokenResponse = readRecord(readRecord(response, "data"), "agent_token");
|
|
211
|
+
const plainToken = readString(tokenResponse, "token");
|
|
212
|
+
if (!args.flags.has("no-store")) {
|
|
213
|
+
store.config.agentToken = plainToken;
|
|
214
|
+
}
|
|
215
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
216
|
+
await store.save();
|
|
217
|
+
io.stdout.write(`Created dev agent token for ${readString(token, "id")}\n` +
|
|
218
|
+
`Token: ${plainToken}\n` +
|
|
219
|
+
(args.flags.has("no-store")
|
|
220
|
+
? ""
|
|
221
|
+
: "Stored local dev token in Arc CLI config. Use only for development.\n"));
|
|
222
|
+
}
|
|
223
|
+
async function agentWhoami(args, env, io) {
|
|
224
|
+
const runtime = await createRuntimeClient(args, env);
|
|
225
|
+
const agent = await runtime.me();
|
|
226
|
+
io.stdout.write(`Agent ${agent.name} (${agent.id})\n`);
|
|
227
|
+
io.stdout.write(`Type: ${agent.type}\n`);
|
|
228
|
+
io.stdout.write(`Environment: ${agent.environment}\n`);
|
|
229
|
+
io.stdout.write(`Status: ${agent.status}\n`);
|
|
230
|
+
}
|
|
231
|
+
async function agentApps(args, env, io) {
|
|
232
|
+
const runtime = await createRuntimeClient(args, env);
|
|
233
|
+
const apps = await runtime.listApps();
|
|
234
|
+
if (apps.length === 0) {
|
|
235
|
+
io.stdout.write("No granted apps.\n");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
for (const app of apps) {
|
|
239
|
+
io.stdout.write(`${app.id} ${app.slug} ${app.name} actions:${app.granted_action_count} verification:${app.arc_verification_status}\n`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function agentActions(args, env, io) {
|
|
243
|
+
const app = requiredFlag(args, "app");
|
|
244
|
+
const runtime = await createRuntimeClient(args, env);
|
|
245
|
+
const appId = await resolveRuntimeAppId(runtime, app);
|
|
246
|
+
const actions = await runtime.listActions(appId);
|
|
247
|
+
if (actions.length === 0) {
|
|
248
|
+
io.stdout.write("No granted actions.\n");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
for (const action of actions) {
|
|
252
|
+
io.stdout.write(`${action.key} decision:${action.decision} risk:${action.risk_level} default:${action.default_decision}\n`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function agentInvoke(args, env, io) {
|
|
256
|
+
const app = requiredFlag(args, "app");
|
|
257
|
+
const actionKey = requiredFlag(args, "action");
|
|
258
|
+
const input = parseJsonFlag(optionalFlag(args, "input") ?? "{}", "input");
|
|
259
|
+
const runtime = await createRuntimeClient(args, env);
|
|
260
|
+
const appId = await resolveRuntimeAppId(runtime, app);
|
|
261
|
+
const result = await runtime.invoke(appId, actionKey, input, {
|
|
262
|
+
idempotencyKey: optionalFlag(args, "idempotency-key") ?? randomUUID()
|
|
263
|
+
});
|
|
264
|
+
if (result.status === "error") {
|
|
265
|
+
throw new Error(`Arc runtime error ${result.error.code}: ${result.error.message}`);
|
|
266
|
+
}
|
|
267
|
+
io.stdout.write(`Invocation ${result.invocation_id ?? "unknown"} status: ${result.status} decision: ${result.decision}\n`);
|
|
268
|
+
if (result.status === "pending_approval" && result.approval_id) {
|
|
269
|
+
io.stdout.write(`Approval required: ${result.approval_id}\n`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function invokeAction(args, env, io) {
|
|
273
|
+
const actionKey = requiredPositional(args, 0, "action key");
|
|
274
|
+
const store = await loadConfig(env);
|
|
275
|
+
const agentToken = optionalFlag(args, "agent-token") ?? store.config.agentToken;
|
|
276
|
+
const app = optionalFlag(args, "app") ?? store.config.currentAppSlug ?? store.config.currentAppId;
|
|
277
|
+
if (!agentToken) {
|
|
278
|
+
throw new Error("No agent token configured. Run arc agent dev-token first.");
|
|
279
|
+
}
|
|
280
|
+
if (!app) {
|
|
281
|
+
throw new Error("No app configured. Pass --app or run arc app create first.");
|
|
282
|
+
}
|
|
283
|
+
const input = parseJsonFlag(optionalFlag(args, "input") ?? "{}", "input");
|
|
284
|
+
const client = new ArcAgentClient({
|
|
285
|
+
agentToken,
|
|
286
|
+
baseUrl: store.config.apiUrl ?? defaultApiUrl
|
|
287
|
+
});
|
|
288
|
+
const response = await client.invoke({
|
|
289
|
+
action_key: actionKey,
|
|
290
|
+
app_id: uuidPattern.test(app) ? app : undefined,
|
|
291
|
+
app_slug: uuidPattern.test(app) ? undefined : app,
|
|
292
|
+
idempotency_key: optionalFlag(args, "idempotency-key") ?? randomUUID(),
|
|
293
|
+
input
|
|
294
|
+
});
|
|
295
|
+
const data = readRecord(response, "data");
|
|
296
|
+
const invocation = readRecord(data, "invocation");
|
|
297
|
+
const approval = maybeRecord(data.approval);
|
|
298
|
+
io.stdout.write(`Invocation ${readString(invocation, "id")} status: ${readString(data, "status")}\n`);
|
|
299
|
+
if (approval) {
|
|
300
|
+
io.stdout.write(`Approval required: ${readString(approval, "id")}\n`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
async function delegationsList(args, env, io) {
|
|
304
|
+
const store = await loadConfig(env);
|
|
305
|
+
const client = await ensureDeveloperClient(store, io);
|
|
306
|
+
const response = await client.listDelegations();
|
|
307
|
+
const delegations = readArray(readRecord(response, "data").delegations);
|
|
308
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
309
|
+
await store.save();
|
|
310
|
+
if (delegations.length === 0) {
|
|
311
|
+
io.stdout.write("No delegations.\n");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
for (const item of delegations) {
|
|
315
|
+
const delegation = readRecordValue(item);
|
|
316
|
+
const agent = readRecord(delegation, "agent");
|
|
317
|
+
const app = readRecord(delegation, "app");
|
|
318
|
+
io.stdout.write(`${readString(delegation, "id")} ${readString(delegation, "status")} ` +
|
|
319
|
+
`${readString(agent, "name")} -> ${readString(app, "slug")}\n`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async function delegationsShow(args, env, io) {
|
|
323
|
+
const delegationId = requiredPositional(args, 0, "delegation id");
|
|
324
|
+
const store = await loadConfig(env);
|
|
325
|
+
const client = await ensureDeveloperClient(store, io);
|
|
326
|
+
const response = await client.getDelegation(delegationId);
|
|
327
|
+
const delegation = readRecord(readRecord(response, "data"), "delegation");
|
|
328
|
+
const agent = readRecord(delegation, "agent");
|
|
329
|
+
const app = readRecord(delegation, "app");
|
|
330
|
+
const matrix = readRecord(delegation, "permission_matrix");
|
|
331
|
+
const actions = readArray(matrix.actions);
|
|
332
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
333
|
+
await store.save();
|
|
334
|
+
io.stdout.write(`Delegation ${readString(delegation, "id")}\n`);
|
|
335
|
+
io.stdout.write(`Status: ${readString(delegation, "status")}\n`);
|
|
336
|
+
io.stdout.write(`Agent: ${readString(agent, "name")}\n`);
|
|
337
|
+
io.stdout.write(`App: ${readString(app, "slug")}\n`);
|
|
338
|
+
for (const item of actions) {
|
|
339
|
+
const action = readRecordValue(item);
|
|
340
|
+
const decision = typeof action.decision === "string" ? action.decision : "block";
|
|
341
|
+
io.stdout.write(`${readString(action, "key")} decision:${decision} risk:${readString(action, "risk_level")}\n`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
async function delegationsCreate(args, env, io) {
|
|
345
|
+
const agentId = requiredFlag(args, "agent");
|
|
346
|
+
const appId = requiredFlag(args, "app");
|
|
347
|
+
const actions = parseJsonFlag(requiredFlag(args, "actions"), "actions");
|
|
348
|
+
const store = await loadConfig(env);
|
|
349
|
+
const client = await ensureDeveloperClient(store, io);
|
|
350
|
+
const response = await client.createDelegation({
|
|
351
|
+
actions: actions,
|
|
352
|
+
agent_id: agentId,
|
|
353
|
+
app_id: appId,
|
|
354
|
+
expires_at: optionalFlag(args, "expires-at") ?? undefined,
|
|
355
|
+
label: optionalFlag(args, "label") ?? undefined,
|
|
356
|
+
name: optionalFlag(args, "name") ?? undefined
|
|
357
|
+
});
|
|
358
|
+
const delegation = readRecord(readRecord(response, "data"), "delegation");
|
|
359
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
360
|
+
await store.save();
|
|
361
|
+
io.stdout.write(`Created delegation ${readString(delegation, "id")}\n`);
|
|
362
|
+
io.stdout.write(`Status: ${readString(delegation, "status")}\n`);
|
|
363
|
+
}
|
|
364
|
+
async function delegationsRevoke(args, env, io) {
|
|
365
|
+
const delegationId = requiredPositional(args, 0, "delegation id");
|
|
366
|
+
const store = await loadConfig(env);
|
|
367
|
+
const client = await ensureDeveloperClient(store, io);
|
|
368
|
+
const response = await client.revokeDelegation(delegationId, {
|
|
369
|
+
reason: optionalFlag(args, "reason") ?? undefined
|
|
370
|
+
});
|
|
371
|
+
const delegation = readRecord(readRecord(response, "data"), "delegation");
|
|
372
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
373
|
+
await store.save();
|
|
374
|
+
io.stdout.write(`Revoked delegation ${readString(delegation, "id")}\n`);
|
|
375
|
+
io.stdout.write(`Status: ${readString(delegation, "status")}\n`);
|
|
376
|
+
}
|
|
377
|
+
async function orgsList(args, env, io) {
|
|
378
|
+
void args;
|
|
379
|
+
const store = await loadConfig(env);
|
|
380
|
+
const client = await ensureDeveloperClient(store, io);
|
|
381
|
+
const response = await client.listOrgs();
|
|
382
|
+
const data = readRecord(response, "data");
|
|
383
|
+
const current = readRecord(data, "current_org");
|
|
384
|
+
const orgs = readArray(data.orgs);
|
|
385
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
386
|
+
store.config.currentOrgId = readString(current, "id");
|
|
387
|
+
await store.save();
|
|
388
|
+
for (const item of orgs) {
|
|
389
|
+
const org = readRecordValue(item);
|
|
390
|
+
const marker = readString(org, "org_id") === store.config.currentOrgId ? "*" : " ";
|
|
391
|
+
io.stdout.write(`${marker} ${readString(org, "org_id")} ${readString(org, "org_slug")} ` +
|
|
392
|
+
`${readString(org, "role")} ${readString(org, "status")}\n`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async function orgsCurrent(args, env, io) {
|
|
396
|
+
void args;
|
|
397
|
+
const store = await loadConfig(env);
|
|
398
|
+
const client = await ensureDeveloperClient(store, io);
|
|
399
|
+
const response = await client.getCurrentOrg();
|
|
400
|
+
const data = readRecord(response, "data");
|
|
401
|
+
const current = readRecord(data, "current_org");
|
|
402
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
403
|
+
store.config.currentOrgId = readString(current, "id");
|
|
404
|
+
await store.save();
|
|
405
|
+
io.stdout.write(`${readString(current, "name")} (${readString(current, "id")})\n`);
|
|
406
|
+
io.stdout.write(`Slug: ${readString(current, "slug")}\n`);
|
|
407
|
+
io.stdout.write(`Role: ${readString(data, "role")}\n`);
|
|
408
|
+
}
|
|
409
|
+
async function orgsMembers(args, env, io) {
|
|
410
|
+
void args;
|
|
411
|
+
const store = await loadConfig(env);
|
|
412
|
+
const client = await ensureDeveloperClient(store, io);
|
|
413
|
+
const response = await client.listOrgMembers();
|
|
414
|
+
const members = readArray(readRecord(response, "data").members);
|
|
415
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
416
|
+
await store.save();
|
|
417
|
+
for (const item of members) {
|
|
418
|
+
const member = readRecordValue(item);
|
|
419
|
+
io.stdout.write(`${readString(member, "user_id")} ${readString(member, "email")} ` +
|
|
420
|
+
`${readString(member, "role")} ${readString(member, "status")}\n`);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
async function orgsInvite(args, env, io) {
|
|
424
|
+
const email = requiredFlag(args, "email");
|
|
425
|
+
const role = requiredFlag(args, "role");
|
|
426
|
+
const name = optionalFlag(args, "name");
|
|
427
|
+
const store = await loadConfig(env);
|
|
428
|
+
const client = await ensureDeveloperClient(store, io);
|
|
429
|
+
const response = await client.inviteOrgMember({ email, name, role });
|
|
430
|
+
const user = readRecord(readRecord(response, "data"), "user");
|
|
431
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
432
|
+
await store.save();
|
|
433
|
+
io.stdout.write(`Invited ${readString(user, "email")} as ${readString(user, "role")}\n`);
|
|
434
|
+
}
|
|
435
|
+
async function createRuntimeClient(args, env) {
|
|
436
|
+
const store = await loadConfig(env);
|
|
437
|
+
const agentToken = optionalFlag(args, "agent-token") ?? env.ARC_AGENT_TOKEN ?? store.config.agentToken;
|
|
438
|
+
if (!agentToken) {
|
|
439
|
+
throw new Error("ARC_AGENT_TOKEN is required for agent runtime commands.");
|
|
440
|
+
}
|
|
441
|
+
return createArcAgentRuntime({
|
|
442
|
+
agentToken,
|
|
443
|
+
apiUrl: trimTrailingSlash(optionalFlag(args, "api-url") ?? env.ARC_API_URL ?? store.config.apiUrl ?? defaultApiUrl)
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
async function resolveRuntimeAppId(runtime, requestedApp) {
|
|
447
|
+
if (uuidPattern.test(requestedApp)) {
|
|
448
|
+
return requestedApp;
|
|
449
|
+
}
|
|
450
|
+
const apps = await runtime.listApps();
|
|
451
|
+
const match = apps.find((app) => app.slug === requestedApp || app.name === requestedApp || app.id === requestedApp);
|
|
452
|
+
if (!match) {
|
|
453
|
+
throw new Error(`Granted app not found: ${requestedApp}`);
|
|
454
|
+
}
|
|
455
|
+
return match.id;
|
|
456
|
+
}
|
|
457
|
+
async function approvalsList(args, env, io) {
|
|
458
|
+
const limit = Number(optionalFlag(args, "limit") ?? "20");
|
|
459
|
+
const store = await loadConfig(env);
|
|
460
|
+
const client = await ensureDeveloperClient(store, io);
|
|
461
|
+
const response = await client.listApprovals();
|
|
462
|
+
const approvals = readArray(readRecord(response, "data").approvals).slice(0, limit);
|
|
463
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
464
|
+
await store.save();
|
|
465
|
+
if (approvals.length === 0) {
|
|
466
|
+
io.stdout.write("No approvals.\n");
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
for (const approval of approvals) {
|
|
470
|
+
const row = readRecordValue(approval);
|
|
471
|
+
const summary = maybeRecord(row.summary);
|
|
472
|
+
io.stdout.write(`${readString(row, "id")} ${readString(row, "status")} ` +
|
|
473
|
+
`${summary ? readString(summary, "text") : ""}\n`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async function approvalsResolve(resolution, args, env, io) {
|
|
477
|
+
const approvalId = requiredPositional(args, 0, "approval id");
|
|
478
|
+
const store = await loadConfig(env);
|
|
479
|
+
const client = await ensureDeveloperClient(store, io);
|
|
480
|
+
const response = resolution === "approve"
|
|
481
|
+
? await client.approveApproval(approvalId)
|
|
482
|
+
: await client.denyApproval(approvalId);
|
|
483
|
+
const approval = readRecord(readRecord(response, "data"), "approval");
|
|
484
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
485
|
+
await store.save();
|
|
486
|
+
io.stdout.write(`${resolution === "approve" ? "Approved" : "Denied"} approval ${readString(approval, "id")}\n`);
|
|
487
|
+
io.stdout.write(`Status: ${readString(approval, "status")}\n`);
|
|
488
|
+
}
|
|
489
|
+
async function auditTail(args, env, io) {
|
|
490
|
+
const store = await loadConfig(env);
|
|
491
|
+
const client = await ensureDeveloperClient(store, io);
|
|
492
|
+
const response = await client.tailAudit({
|
|
493
|
+
limit: Number(optionalFlag(args, "limit") ?? "20")
|
|
494
|
+
});
|
|
495
|
+
const events = readArray(readRecord(response, "data").events);
|
|
496
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
497
|
+
await store.save();
|
|
498
|
+
if (events.length === 0) {
|
|
499
|
+
io.stdout.write("No audit events.\n");
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
for (const event of events) {
|
|
503
|
+
const row = readRecordValue(event);
|
|
504
|
+
const createdAt = readString(row, "created_at");
|
|
505
|
+
const eventType = readString(row, "event_type");
|
|
506
|
+
const actorType = readString(row, "actor_type");
|
|
507
|
+
const reason = typeof row.reason === "string" ? row.reason : "";
|
|
508
|
+
io.stdout.write(`${createdAt} ${eventType} ${actorType}${reason ? ` ${reason}` : ""}\n`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function auditExport(args, env, io) {
|
|
512
|
+
const store = await loadConfig(env);
|
|
513
|
+
const client = await ensureDeveloperClient(store, io);
|
|
514
|
+
const input = auditInputFromArgs(args);
|
|
515
|
+
const response = await client.exportAudit(input);
|
|
516
|
+
const data = readRecord(response, "data");
|
|
517
|
+
const content = readString(data, "content");
|
|
518
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
519
|
+
await store.save();
|
|
520
|
+
io.stdout.write(`${content}${content.endsWith("\n") ? "" : "\n"}`);
|
|
521
|
+
}
|
|
522
|
+
async function auditVerify(args, env, io) {
|
|
523
|
+
const store = await loadConfig(env);
|
|
524
|
+
const client = await ensureDeveloperClient(store, io);
|
|
525
|
+
const response = await client.verifyAudit(auditInputFromArgs(args));
|
|
526
|
+
const verification = readRecord(readRecord(response, "data"), "verification");
|
|
527
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
528
|
+
await store.save();
|
|
529
|
+
io.stdout.write(`Audit verification: ${readString(verification, "status")}\n`);
|
|
530
|
+
io.stdout.write(`Continuity: ${readString(verification, "continuity")}\n`);
|
|
531
|
+
io.stdout.write(`Events: ${String(verification.event_count ?? 0)}\n`);
|
|
532
|
+
const mismatches = Array.isArray(verification.mismatches) ? verification.mismatches.length : 0;
|
|
533
|
+
const gaps = Array.isArray(verification.gaps) ? verification.gaps.length : 0;
|
|
534
|
+
io.stdout.write(`Mismatches: ${mismatches}\n`);
|
|
535
|
+
io.stdout.write(`Scoped gaps: ${gaps}\n`);
|
|
536
|
+
io.stdout.write("Evidence: tamper-evident inside Arc DB unless externally anchored.\n");
|
|
537
|
+
}
|
|
538
|
+
async function auditTimeline(args, env, io) {
|
|
539
|
+
const store = await loadConfig(env);
|
|
540
|
+
const client = await ensureDeveloperClient(store, io);
|
|
541
|
+
const response = await client.getAuditTimeline(auditInputFromArgs(args));
|
|
542
|
+
const timeline = readRecord(readRecord(response, "data"), "timeline");
|
|
543
|
+
const events = readArray(timeline.events);
|
|
544
|
+
store.config.sessionCookie = client.cookie ?? store.config.sessionCookie;
|
|
545
|
+
await store.save();
|
|
546
|
+
if (events.length === 0) {
|
|
547
|
+
io.stdout.write("No timeline events.\n");
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
for (const item of events) {
|
|
551
|
+
const event = readRecordValue(item);
|
|
552
|
+
io.stdout.write(`${readString(event, "created_at")} ${readString(event, "event_type")} ` +
|
|
553
|
+
`${String(event.actor_type ?? "")} ${String(event.reason ?? "")}\n`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function devSmoke(args, env, io) {
|
|
557
|
+
const store = await loadConfig(env);
|
|
558
|
+
const apiUrl = trimTrailingSlash(optionalFlag(args, "api-url") ?? store.config.apiUrl ?? defaultApiUrl);
|
|
559
|
+
const response = await fetch(`${apiUrl}/healthz`);
|
|
560
|
+
if (!response.ok) {
|
|
561
|
+
throw new Error(`Arc API health check failed with HTTP ${response.status}.`);
|
|
562
|
+
}
|
|
563
|
+
io.stdout.write(`Arc API healthy at ${apiUrl}\n`);
|
|
564
|
+
}
|
|
565
|
+
async function ensureDeveloperClient(store, io, env = process.env) {
|
|
566
|
+
const client = new ArcDeveloperClient({
|
|
567
|
+
baseUrl: store.config.apiUrl ?? defaultApiUrl,
|
|
568
|
+
orgId: env.ARC_ORG_ID ?? store.config.currentOrgId,
|
|
569
|
+
sessionCookie: store.config.sessionCookie
|
|
570
|
+
});
|
|
571
|
+
if (store.config.sessionCookie) {
|
|
572
|
+
try {
|
|
573
|
+
await client.me();
|
|
574
|
+
return client;
|
|
575
|
+
}
|
|
576
|
+
catch (error) {
|
|
577
|
+
if (!(error instanceof ArcHttpError && error.status === 401)) {
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const email = store.config.devEmail ?? defaultDevEmail;
|
|
583
|
+
await client.devLogin({
|
|
584
|
+
email,
|
|
585
|
+
name: "Arc CLI Dev"
|
|
586
|
+
});
|
|
587
|
+
store.config.sessionCookie = client.cookie ?? undefined;
|
|
588
|
+
await store.save();
|
|
589
|
+
io.stderr.write(`Using Arc dev login as ${email}\n`);
|
|
590
|
+
return client;
|
|
591
|
+
}
|
|
592
|
+
async function resolveAppId(client, config, requestedApp) {
|
|
593
|
+
if (!requestedApp && config.currentAppId) {
|
|
594
|
+
return config.currentAppId;
|
|
595
|
+
}
|
|
596
|
+
const app = requestedApp ?? config.currentAppSlug;
|
|
597
|
+
if (!app) {
|
|
598
|
+
throw new Error("No app configured. Pass --app or run arc app create first.");
|
|
599
|
+
}
|
|
600
|
+
if (uuidPattern.test(app)) {
|
|
601
|
+
return app;
|
|
602
|
+
}
|
|
603
|
+
const response = await client.listApps();
|
|
604
|
+
const apps = readArray(readRecord(readRecord(response, "data"), "apps"));
|
|
605
|
+
const match = apps
|
|
606
|
+
.map(readRecordValue)
|
|
607
|
+
.find((item) => item.slug === app || item.name === app || item.id === app);
|
|
608
|
+
if (!match) {
|
|
609
|
+
throw new Error(`App not found: ${app}`);
|
|
610
|
+
}
|
|
611
|
+
return readString(match, "id");
|
|
612
|
+
}
|
|
613
|
+
async function loadActionsFile(file) {
|
|
614
|
+
const absolute = resolve(file);
|
|
615
|
+
const extension = extname(absolute);
|
|
616
|
+
let imported;
|
|
617
|
+
if (extension === ".ts" || extension === ".tsx") {
|
|
618
|
+
imported = await importTranspiledTypescript(absolute);
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
imported = await import(`${pathToFileURL(absolute).href}?v=${Date.now()}`);
|
|
622
|
+
}
|
|
623
|
+
const module = readRecordValue(imported);
|
|
624
|
+
const actions = module.actions ?? module.default;
|
|
625
|
+
if (!actions || typeof actions !== "object") {
|
|
626
|
+
throw new Error("Actions file must export actions or a default action object.");
|
|
627
|
+
}
|
|
628
|
+
return actions;
|
|
629
|
+
}
|
|
630
|
+
async function importTranspiledTypescript(absolute) {
|
|
631
|
+
const typescript = await import("typescript");
|
|
632
|
+
const source = await readFile(absolute, "utf8");
|
|
633
|
+
const output = typescript.transpileModule(source, {
|
|
634
|
+
compilerOptions: {
|
|
635
|
+
esModuleInterop: true,
|
|
636
|
+
module: typescript.ModuleKind.ES2022,
|
|
637
|
+
moduleResolution: typescript.ModuleResolutionKind.NodeNext,
|
|
638
|
+
target: typescript.ScriptTarget.ES2022
|
|
639
|
+
},
|
|
640
|
+
fileName: absolute
|
|
641
|
+
}).outputText;
|
|
642
|
+
const tempFile = join(dirname(absolute), `.arc-actions-${process.pid}-${Date.now()}.mjs`);
|
|
643
|
+
await writeFile(tempFile, output, "utf8");
|
|
644
|
+
try {
|
|
645
|
+
return await import(`${pathToFileURL(tempFile).href}?v=${Date.now()}`);
|
|
646
|
+
}
|
|
647
|
+
finally {
|
|
648
|
+
await unlink(tempFile).catch(() => undefined);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function auditInputFromArgs(args) {
|
|
652
|
+
return {
|
|
653
|
+
action_id: optionalFlag(args, "action-id"),
|
|
654
|
+
action_key: optionalFlag(args, "action") ?? optionalFlag(args, "action-key"),
|
|
655
|
+
actor_id: optionalFlag(args, "actor") ?? optionalFlag(args, "actor-id"),
|
|
656
|
+
agent_id: optionalFlag(args, "agent") ?? optionalFlag(args, "agent-id"),
|
|
657
|
+
app_id: optionalFlag(args, "app") ?? optionalFlag(args, "app-id"),
|
|
658
|
+
approval_id: optionalFlag(args, "approval") ?? optionalFlag(args, "approval-id"),
|
|
659
|
+
decision: parseAuditDecisionFlag(optionalFlag(args, "decision")),
|
|
660
|
+
delegation_id: optionalFlag(args, "delegation") ?? optionalFlag(args, "delegation-id"),
|
|
661
|
+
event_type: optionalFlag(args, "event-type"),
|
|
662
|
+
format: parseAuditFormatFlag(optionalFlag(args, "format")),
|
|
663
|
+
from: optionalFlag(args, "from"),
|
|
664
|
+
invocation_id: optionalFlag(args, "invocation") ?? optionalFlag(args, "invocation-id"),
|
|
665
|
+
limit: optionalNumberFlag(args, "limit"),
|
|
666
|
+
offset: optionalNumberFlag(args, "offset"),
|
|
667
|
+
status: optionalFlag(args, "status"),
|
|
668
|
+
to: optionalFlag(args, "to"),
|
|
669
|
+
user_id: optionalFlag(args, "user") ?? optionalFlag(args, "user-id")
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function parseAuditFormatFlag(value) {
|
|
673
|
+
if (!value) {
|
|
674
|
+
return undefined;
|
|
675
|
+
}
|
|
676
|
+
if (value === "jsonl" || value === "csv" || value === "bundle") {
|
|
677
|
+
return value;
|
|
678
|
+
}
|
|
679
|
+
throw new Error("--format must be jsonl, csv, or bundle.");
|
|
680
|
+
}
|
|
681
|
+
function parseAuditDecisionFlag(value) {
|
|
682
|
+
if (!value) {
|
|
683
|
+
return undefined;
|
|
684
|
+
}
|
|
685
|
+
if (value === "allow" || value === "ask" || value === "block") {
|
|
686
|
+
return value;
|
|
687
|
+
}
|
|
688
|
+
throw new Error("--decision must be allow, ask, or block.");
|
|
689
|
+
}
|
|
690
|
+
function optionalNumberFlag(args, name) {
|
|
691
|
+
const value = optionalFlag(args, name);
|
|
692
|
+
if (value === undefined) {
|
|
693
|
+
return undefined;
|
|
694
|
+
}
|
|
695
|
+
const parsed = Number(value);
|
|
696
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
697
|
+
throw new Error(`--${name} must be a non-negative integer.`);
|
|
698
|
+
}
|
|
699
|
+
return parsed;
|
|
700
|
+
}
|
|
701
|
+
async function loadConfig(env) {
|
|
702
|
+
const configDir = env.ARC_CONFIG_HOME ? resolve(env.ARC_CONFIG_HOME) : join(homedir(), ".arc");
|
|
703
|
+
const configPath = join(configDir, "config.json");
|
|
704
|
+
let config = {};
|
|
705
|
+
try {
|
|
706
|
+
config = JSON.parse(await readFile(configPath, "utf8"));
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
if (error.code !== "ENOENT") {
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
config,
|
|
715
|
+
path: configPath,
|
|
716
|
+
save: async () => {
|
|
717
|
+
await mkdir(configDir, { mode: 0o700, recursive: true });
|
|
718
|
+
await chmod(configDir, 0o700).catch(() => undefined);
|
|
719
|
+
await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, {
|
|
720
|
+
encoding: "utf8",
|
|
721
|
+
mode: 0o600
|
|
722
|
+
});
|
|
723
|
+
await chmod(configPath, 0o600).catch(() => undefined);
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function parseArgs(argv) {
|
|
728
|
+
const flags = new Map();
|
|
729
|
+
const positionals = [];
|
|
730
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
731
|
+
const item = argv[index];
|
|
732
|
+
if (!item) {
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (item.startsWith("--")) {
|
|
736
|
+
const [rawName, inlineValue] = item.slice(2).split("=", 2);
|
|
737
|
+
const name = rawName ?? "";
|
|
738
|
+
if (!name) {
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
if (inlineValue !== undefined) {
|
|
742
|
+
flags.set(name, inlineValue);
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
const next = argv[index + 1];
|
|
746
|
+
if (next && !next.startsWith("--")) {
|
|
747
|
+
flags.set(name, next);
|
|
748
|
+
index += 1;
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
flags.set(name, true);
|
|
752
|
+
}
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
positionals.push(item);
|
|
756
|
+
}
|
|
757
|
+
return { flags, positionals };
|
|
758
|
+
}
|
|
759
|
+
function requiredFlag(args, name) {
|
|
760
|
+
const value = optionalFlag(args, name);
|
|
761
|
+
if (!value) {
|
|
762
|
+
throw new Error(`Missing required --${name}.`);
|
|
763
|
+
}
|
|
764
|
+
return value;
|
|
765
|
+
}
|
|
766
|
+
function optionalFlag(args, name) {
|
|
767
|
+
const value = args.flags.get(name);
|
|
768
|
+
return typeof value === "string" ? value : undefined;
|
|
769
|
+
}
|
|
770
|
+
function requiredPositional(args, index, label) {
|
|
771
|
+
const value = args.positionals[index];
|
|
772
|
+
if (!value) {
|
|
773
|
+
throw new Error(`Missing ${label}.`);
|
|
774
|
+
}
|
|
775
|
+
return value;
|
|
776
|
+
}
|
|
777
|
+
function parseJsonFlag(value, label) {
|
|
778
|
+
try {
|
|
779
|
+
const parsed = JSON.parse(value);
|
|
780
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
781
|
+
throw new Error("not object");
|
|
782
|
+
}
|
|
783
|
+
return parsed;
|
|
784
|
+
}
|
|
785
|
+
catch {
|
|
786
|
+
throw new Error(`--${label} must be a JSON object.`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function sanitizeConfig(config) {
|
|
790
|
+
return {
|
|
791
|
+
...config,
|
|
792
|
+
agentToken: config.agentToken ? maskSecret(config.agentToken, "stored dev token") : undefined,
|
|
793
|
+
sessionCookie: config.sessionCookie ? maskSecret(config.sessionCookie, "stored session cookie") : undefined
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function maskSecret(value, label) {
|
|
797
|
+
if (value.length < 24) {
|
|
798
|
+
return `[${label}: hidden]`;
|
|
799
|
+
}
|
|
800
|
+
return `[${label}: ${value.slice(0, 6)}...${value.slice(-4)}]`;
|
|
801
|
+
}
|
|
802
|
+
function readRecord(input, key) {
|
|
803
|
+
const parent = readRecordValue(input);
|
|
804
|
+
return readRecordValue(parent[key]);
|
|
805
|
+
}
|
|
806
|
+
function maybeRecord(input) {
|
|
807
|
+
return input && typeof input === "object" && !Array.isArray(input)
|
|
808
|
+
? input
|
|
809
|
+
: null;
|
|
810
|
+
}
|
|
811
|
+
function readRecordValue(input) {
|
|
812
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
813
|
+
throw new Error("Arc response was missing an expected object.");
|
|
814
|
+
}
|
|
815
|
+
return input;
|
|
816
|
+
}
|
|
817
|
+
function readArray(input) {
|
|
818
|
+
if (!Array.isArray(input)) {
|
|
819
|
+
throw new Error("Arc response was missing an expected array.");
|
|
820
|
+
}
|
|
821
|
+
return input;
|
|
822
|
+
}
|
|
823
|
+
function readString(input, key) {
|
|
824
|
+
const value = input[key];
|
|
825
|
+
if (typeof value !== "string") {
|
|
826
|
+
throw new Error(`Arc response was missing ${key}.`);
|
|
827
|
+
}
|
|
828
|
+
return value;
|
|
829
|
+
}
|
|
830
|
+
function printHelp(io) {
|
|
831
|
+
io.stdout.write(`Arc CLI
|
|
832
|
+
|
|
833
|
+
Trust layer tooling for high-risk AI actions.
|
|
834
|
+
From this monorepo after npm run build, run:
|
|
835
|
+
node packages/arc-sdk/dist/cli.js <command>
|
|
836
|
+
Installed package usage:
|
|
837
|
+
arc <command>
|
|
838
|
+
|
|
839
|
+
Commands:
|
|
840
|
+
arc config set --api-url <url> [--dev-email <email>] [--org <id>] Save local Arc API config
|
|
841
|
+
arc config get Show config with secrets masked
|
|
842
|
+
arc app create <name> --execute-url <url> [--slug <s>] Register an app and local app connection
|
|
843
|
+
arc actions sync <file> [--app <id-or-slug>] Sync arc.defineActions output
|
|
844
|
+
arc actions sync <file> --dry-run Print the sync payload only
|
|
845
|
+
arc agent dev-token [--agent-type <type>] Create a local dev agent token
|
|
846
|
+
arc agent whoami Show agent runtime identity
|
|
847
|
+
arc agent apps List apps granted to ARC_AGENT_TOKEN
|
|
848
|
+
arc agent actions --app <id-or-slug> List granted app actions
|
|
849
|
+
arc agent invoke --app <id-or-slug> --action <key> Request an action through Arc
|
|
850
|
+
arc invoke <action-key> --app <id-or-slug> --input '{}' Invoke through Arc
|
|
851
|
+
arc delegations list List Agentic SSO delegations
|
|
852
|
+
arc delegations show <delegation-id> Show delegated authority
|
|
853
|
+
arc delegations create --agent <id> --app <id> --actions '{"read":"allow"}'
|
|
854
|
+
arc delegations revoke <delegation-id> Revoke delegated authority
|
|
855
|
+
arc orgs list List available workspaces
|
|
856
|
+
arc orgs current Show current workspace context
|
|
857
|
+
arc orgs members List current workspace members
|
|
858
|
+
arc orgs invite --email <email> --role <role> Invite a member to the workspace
|
|
859
|
+
arc approvals list List pending/resolved approvals
|
|
860
|
+
arc approvals approve <approval-id> Approve and queue execution
|
|
861
|
+
arc approvals deny <approval-id> Deny without execution
|
|
862
|
+
arc audit tail [--limit <n>] Show recent redacted audit events
|
|
863
|
+
arc audit export --format jsonl|csv [--agent <id>] Export redacted audit evidence
|
|
864
|
+
arc audit verify [--agent <id>] Verify audit hashes for a scope
|
|
865
|
+
arc audit timeline --invocation <id> Show an incident timeline
|
|
866
|
+
arc dev smoke Check Arc API health
|
|
867
|
+
|
|
868
|
+
Local proof:
|
|
869
|
+
ARC_API_URL=http://127.0.0.1:<dev-port> npm --workspace @geostack/ai-support-ops-reference run smoke:dev
|
|
870
|
+
`);
|
|
871
|
+
}
|
|
872
|
+
function printError(error, io) {
|
|
873
|
+
if (error instanceof ArcHttpError) {
|
|
874
|
+
io.stderr.write(`Arc API error ${error.status} ${error.code}: ${error.message}\n`);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
if (error instanceof Error) {
|
|
878
|
+
io.stderr.write(`${error.message}\n`);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
io.stderr.write("Unknown Arc CLI error.\n");
|
|
882
|
+
}
|
|
883
|
+
function trimTrailingSlash(value) {
|
|
884
|
+
return value.replace(/\/+$/u, "");
|
|
885
|
+
}
|
|
886
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
887
|
+
const code = await main();
|
|
888
|
+
process.exitCode = code;
|
|
889
|
+
}
|
|
890
|
+
//# sourceMappingURL=cli.js.map
|