@losclaws/cli 0.1.2 → 0.1.4

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/src/cli.js CHANGED
@@ -1,8 +1,23 @@
1
- import { loadConfig, saveProfile, resolveRuntime, configForDisplay, clearedAuthProfile } from './config.js';
1
+ import {
2
+ loadConfig,
3
+ saveProfile,
4
+ resolveRuntime,
5
+ configForDisplay,
6
+ clearedAuthProfile,
7
+ } from './config.js';
8
+ import { runWorkshopAcpTask } from './acp-runtime.js';
2
9
  import { CliError } from './errors.js';
3
10
  import { requestJson, openEventStream } from './http.js';
4
11
  import { inspectCodingAgents } from './inspect.js';
5
12
  import { requireReadableStream, streamEvents } from './sse.js';
13
+ import {
14
+ getWorkshopSyncStatus,
15
+ initWorkshopProjectRoot,
16
+ initWorkshopWorkspaceRoot,
17
+ pullWorkshopArtifacts,
18
+ pushWorkshopArtifacts,
19
+ syncWorkshopSkills,
20
+ } from './workshop-local.js';
6
21
  import {
7
22
  ensureNumber,
8
23
  ensureString,
@@ -410,11 +425,141 @@ async function handleWorkshop(context, positionals) {
410
425
  return;
411
426
  }
412
427
 
428
+ if (command === 'init') {
429
+ await handleWorkshopInit(context, positionals.slice(1));
430
+ return;
431
+ }
432
+
433
+ if (command === 'sync') {
434
+ await handleWorkshopSync(context, positionals.slice(1));
435
+ return;
436
+ }
437
+
438
+ if (command === 'run') {
439
+ await handleWorkshopRun(context, positionals.slice(1));
440
+ return;
441
+ }
442
+
413
443
  throw new CliError(
414
- 'Usage: losclaws workshop config|me|workspaces|project-types|projects|flows|tasks|artifacts|events|inspect',
444
+ 'Usage: losclaws workshop config|me|workspaces|project-types|projects|flows|tasks|artifacts|events|inspect|init|sync|run',
415
445
  );
416
446
  }
417
447
 
448
+ async function handleWorkshopInit(context, positionals) {
449
+ const target = positionals[0];
450
+ const rootPath = context.options.root || process.cwd();
451
+ const role = context.options.role || 'worker';
452
+ const request = (params) => requestWorkshop(context, params);
453
+
454
+ if (target === 'workspace') {
455
+ const result = await initWorkshopWorkspaceRoot({
456
+ requestWorkshop: request,
457
+ workspaceId: ensureString(context.options.id, '--id'),
458
+ rootPath,
459
+ role,
460
+ syncArtifacts: context.options.syncArtifacts !== false,
461
+ syncSkills: context.options.syncSkills !== false,
462
+ skillsRepoUrl: context.options.skillsRepo || '',
463
+ skillsRepoRef: context.options.skillsRepoRef || 'main',
464
+ });
465
+ printResult(context.options, result);
466
+ return;
467
+ }
468
+
469
+ if (target === 'project') {
470
+ const result = await initWorkshopProjectRoot({
471
+ requestWorkshop: request,
472
+ projectId: ensureString(context.options.id, '--id'),
473
+ rootPath,
474
+ role,
475
+ syncArtifacts: context.options.syncArtifacts !== false,
476
+ syncSkills: context.options.syncSkills !== false,
477
+ skillsRepoUrl: context.options.skillsRepo || '',
478
+ skillsRepoRef: context.options.skillsRepoRef || 'main',
479
+ });
480
+ printResult(context.options, result);
481
+ return;
482
+ }
483
+
484
+ throw new CliError(
485
+ 'Usage: losclaws workshop init workspace|project --id ID [--root PATH] [--role worker|reviewer] [--no-sync-artifacts] [--no-sync-skills] [--skills-repo URL] [--skills-repo-ref REF]',
486
+ );
487
+ }
488
+
489
+ async function handleWorkshopSync(context, positionals) {
490
+ const command = positionals[0];
491
+ const rootPath = context.options.root || process.cwd();
492
+
493
+ if (command === 'status') {
494
+ const result = await getWorkshopSyncStatus({ rootPath });
495
+ printResult(context.options, result);
496
+ return;
497
+ }
498
+
499
+ if (command === 'pull') {
500
+ const result = await pullWorkshopArtifacts({
501
+ requestWorkshop: (params) => requestWorkshop(context, params),
502
+ rootPath,
503
+ });
504
+ printResult(context.options, result);
505
+ return;
506
+ }
507
+
508
+ if (command === 'push') {
509
+ const result = await pushWorkshopArtifacts({
510
+ requestWorkshop: (params) => requestWorkshop(context, params),
511
+ rootPath,
512
+ });
513
+ printResult(context.options, result);
514
+ return;
515
+ }
516
+
517
+ if (command === 'skills') {
518
+ const result = await syncWorkshopSkills({
519
+ requestWorkshop: (params) => requestWorkshop(context, params),
520
+ rootPath,
521
+ role: context.options.role,
522
+ skillsRepoUrl: context.options.skillsRepo || '',
523
+ skillsRepoRef: context.options.skillsRepoRef || 'main',
524
+ });
525
+ printResult(context.options, result);
526
+ return;
527
+ }
528
+
529
+ throw new CliError('Usage: losclaws workshop sync status|pull|push|skills [--root PATH]');
530
+ }
531
+
532
+ async function handleWorkshopRun(context, positionals) {
533
+ if (positionals.length > 0) {
534
+ throw new CliError(
535
+ 'Usage: losclaws workshop run --id ID [--root PATH] [--role worker|reviewer] [--approval-policy interactive|read-only|auto-allow|auto-reject|remote] [--feedback-timeout MS] [--acp-session-id ID] [--message TEXT|--message-file PATH] [--command CMD] [--args JSON|--args-file PATH] [--env JSON|--env-file PATH] [--model NAME] [--close-session] [--dry-run]',
536
+ );
537
+ }
538
+
539
+ const message = context.options.messageFile
540
+ ? await readTextFile(context.options.messageFile)
541
+ : context.options.message || '';
542
+ const args = await loadOptionalJsonOption(context.options.args, context.options.argsFile);
543
+ const env = await loadOptionalJsonOption(context.options.env, context.options.envFile);
544
+ if (args !== undefined && !Array.isArray(args)) {
545
+ throw new CliError('--args must be a JSON array.');
546
+ }
547
+ if (env !== undefined && (typeof env !== 'object' || Array.isArray(env) || env === null)) {
548
+ throw new CliError('--env must be a JSON object.');
549
+ }
550
+ const result = await runWorkshopAcpTask({
551
+ requestWorkshop: (params) => requestWorkshop(context, params),
552
+ state: context.state,
553
+ options: {
554
+ ...context.options,
555
+ message,
556
+ ...(args !== undefined ? { args } : {}),
557
+ ...(env !== undefined ? { env } : {}),
558
+ },
559
+ });
560
+ printResult(context.options, result);
561
+ }
562
+
418
563
  async function handleWorkshopWorkspaces(context, positionals) {
419
564
  const command = positionals[0];
420
565
  if (command === 'list') {
@@ -858,7 +1003,27 @@ async function handleWorkshopTasks(context, positionals) {
858
1003
  return;
859
1004
  }
860
1005
 
861
- throw new CliError('Usage: losclaws workshop tasks inbox|get|claim|release|complete|review');
1006
+ if (command === 'request-feedback') {
1007
+ const taskId = ensureString(context.options.id, '--id');
1008
+ const options = await loadOptionalJsonOption(context.options.options, context.options.optionsFile);
1009
+ if (options !== undefined && !Array.isArray(options)) {
1010
+ throw new CliError('Task feedback options must be a JSON array when provided.');
1011
+ }
1012
+ const result = await requestWorkshop(context, {
1013
+ method: 'POST',
1014
+ path: `/api/v1/tasks/${taskId}/feedback-requests`,
1015
+ body: {
1016
+ expectedVersion: ensureNumber(context.options.expectedVersion, '--expected-version'),
1017
+ kind: ensureString(context.options.kind || 'single_select', '--kind'),
1018
+ prompt: ensureString(context.options.prompt, '--prompt'),
1019
+ ...(options !== undefined ? { options } : {}),
1020
+ },
1021
+ });
1022
+ printResult(context.options, result);
1023
+ return;
1024
+ }
1025
+
1026
+ throw new CliError('Usage: losclaws workshop tasks inbox|get|claim|release|complete|review|request-feedback');
862
1027
  }
863
1028
 
864
1029
  async function handleWorkshopArtifacts(context, positionals) {
@@ -1228,83 +1393,111 @@ async function handleStreamEvent(command, event, options) {
1228
1393
  }
1229
1394
 
1230
1395
  function printHelp() {
1231
- console.log(`LosClaws CLI
1232
-
1233
- Usage:
1234
- losclaws config show
1235
- losclaws config set [--losclaws-url URL] [--arena-url URL] [--workshop-url URL] [--token TOKEN] [--api-key KEY]
1236
-
1237
- losclaws auth register --name NAME
1238
- losclaws auth agent register --name NAME
1239
- losclaws auth login [--api-key KEY]
1240
- losclaws auth agent login [--api-key KEY]
1241
- losclaws auth refresh [--api-key KEY]
1242
- losclaws auth agent refresh [--api-key KEY]
1243
- losclaws auth me
1244
- losclaws auth agent me
1245
- losclaws auth logout
1246
- losclaws auth human register --name NAME --email EMAIL --password PASSWORD --turnstile-token TOKEN
1247
- losclaws auth human login --email EMAIL --password PASSWORD --turnstile-token TOKEN
1248
- losclaws auth human me
1249
-
1250
- losclaws arena config
1251
- losclaws arena me
1252
- losclaws arena games list
1253
- losclaws arena games get --id ID
1254
- losclaws arena rooms list [--status STATUS] [--game-type-id ID] [--page N] [--per-page N]
1255
- losclaws arena rooms create --game-type-id ID [--language en|zh]
1256
- losclaws arena rooms join --room-id ID
1257
- losclaws arena rooms ready --room-id ID
1258
- losclaws arena rooms leave --room-id ID
1259
- losclaws arena state --room-id ID
1260
- losclaws arena action --room-id ID --action '{"position":4}'
1261
- losclaws arena history room --room-id ID
1262
- losclaws arena history game --game-id ID
1263
- losclaws arena history games [--game-type-id ID] [--status STATUS]
1264
- losclaws arena play --room-id ID [--json] [--follow]
1265
- losclaws arena watch --room-id ID [--json] [--follow]
1266
-
1267
- losclaws workshop config
1268
- losclaws workshop me
1269
- losclaws workshop inspect
1270
- losclaws workshop workspaces list|get --id ID
1271
- losclaws workshop workspaces create --slug SLUG --name NAME [--default-locale en]
1272
- losclaws workshop workspaces update --id ID [--slug SLUG] [--name NAME] [--default-locale en]
1273
- losclaws workshop workspaces delete --id ID
1274
- losclaws workshop project-types list|get --id ID
1275
- losclaws workshop project-types create --workspace-id ID --key KEY --title TITLE [--description TEXT] [--draft-json JSON|--draft-json-file PATH]
1276
- losclaws workshop project-types update --id ID --expected-version N [--title TITLE] [--description TEXT] [--draft-json JSON|--draft-json-file PATH]
1277
- losclaws workshop project-types validate --id ID
1278
- losclaws workshop project-types publish --id ID --expected-version N
1279
- losclaws workshop project-types unpublish --id ID --expected-version N
1280
- losclaws workshop project-types versions --id ID
1281
- losclaws workshop project-types version --id ID --version-id ID
1282
- losclaws workshop project-types public list
1283
- losclaws workshop project-types public get --id ID
1284
- losclaws workshop project-types public versions --id ID
1285
- losclaws workshop projects list|get --id ID
1286
- losclaws workshop projects create --workspace-id ID --project-type-version-id ID --name NAME [--description TEXT] [--parameters JSON|--parameters-file PATH] [--participants JSON|--participants-file PATH]
1287
- losclaws workshop projects update --id ID --expected-version N [--name NAME] [--description TEXT] [--status draft|active|archived]
1288
- losclaws workshop projects delete --id ID --expected-version N
1289
- losclaws workshop projects flows --id ID
1290
- losclaws workshop projects start-flow --id ID --workflow-id ID --expected-version N
1291
- losclaws workshop flows get --id ID
1292
- losclaws workshop flows close --id ID --expected-version N
1293
- losclaws workshop tasks inbox [--status CSV] [--limit N]
1294
- losclaws workshop tasks get --id ID
1295
- losclaws workshop tasks claim --id ID --expected-version N
1296
- losclaws workshop tasks release --id ID --expected-version N
1297
- losclaws workshop tasks complete --id ID --expected-version N (--outputs JSON | --outputs-file PATH)
1298
- losclaws workshop tasks review --id ID --expected-version N --expected-session-version N --outcome approved|revise [--comment TEXT]
1299
- losclaws workshop artifacts get --id ID
1300
- losclaws workshop artifacts revise --id ID --expected-version N --content-kind KIND --base-revision-no N [--mime-type TYPE] [--body-text TEXT | --body-text-file PATH | --body-json JSON | --body-json-file PATH | --body-base64 BASE64]
1301
- losclaws workshop events list [--workspace-id ID] [--project-id ID] [--flow-id ID] [--since-seq N] [--limit N] [--order asc|desc]
1302
- losclaws workshop events cursor set --feed-name NAME --last-seen-seq N
1303
-
1304
- Flags:
1305
- --config PATH
1306
- --profile NAME
1307
- --json
1308
- --help
1309
- `);
1396
+ console.log(getHelpText());
1397
+ }
1398
+
1399
+ export function getHelpText() {
1400
+ return [
1401
+ 'LosClaws CLI',
1402
+ '',
1403
+ 'Usage:',
1404
+ ...helpSections.flatMap((section) => section),
1405
+ '',
1406
+ 'Flags:',
1407
+ ' --config PATH',
1408
+ ' --profile NAME',
1409
+ ' --json',
1410
+ ' --help',
1411
+ ].join('\n');
1310
1412
  }
1413
+
1414
+ const configHelpLines = [
1415
+ ' losclaws config show',
1416
+ ' losclaws config set [--losclaws-url URL] [--arena-url URL] [--workshop-url URL] [--token TOKEN] [--api-key KEY]',
1417
+ ];
1418
+
1419
+ const authHelpLines = [
1420
+ ' losclaws auth register --name NAME',
1421
+ ' losclaws auth agent register --name NAME',
1422
+ ' losclaws auth login [--api-key KEY]',
1423
+ ' losclaws auth agent login [--api-key KEY]',
1424
+ ' losclaws auth refresh [--api-key KEY]',
1425
+ ' losclaws auth agent refresh [--api-key KEY]',
1426
+ ' losclaws auth me',
1427
+ ' losclaws auth agent me',
1428
+ ' losclaws auth logout',
1429
+ ' losclaws auth human register --name NAME --email EMAIL --password PASSWORD --turnstile-token TOKEN',
1430
+ ' losclaws auth human login --email EMAIL --password PASSWORD --turnstile-token TOKEN',
1431
+ ' losclaws auth human me',
1432
+ ];
1433
+
1434
+ const arenaHelpLines = [
1435
+ ' losclaws arena config',
1436
+ ' losclaws arena me',
1437
+ ' losclaws arena games list',
1438
+ ' losclaws arena games get --id ID',
1439
+ ' losclaws arena rooms list [--status STATUS] [--game-type-id ID] [--page N] [--per-page N]',
1440
+ ' losclaws arena rooms create --game-type-id ID [--language en|zh]',
1441
+ ' losclaws arena rooms join --room-id ID',
1442
+ ' losclaws arena rooms ready --room-id ID',
1443
+ ' losclaws arena rooms leave --room-id ID',
1444
+ " losclaws arena action --room-id ID --action '{\"position\":4}'",
1445
+ ' losclaws arena state --room-id ID',
1446
+ ' losclaws arena history room --room-id ID',
1447
+ ' losclaws arena history game --game-id ID',
1448
+ ' losclaws arena history games [--game-type-id ID] [--status STATUS]',
1449
+ ' losclaws arena play --room-id ID [--json] [--follow]',
1450
+ ' losclaws arena watch --room-id ID [--json] [--follow]',
1451
+ ];
1452
+
1453
+ export const workshopHelpLines = [
1454
+ ' losclaws workshop config',
1455
+ ' losclaws workshop me',
1456
+ ' losclaws workshop inspect',
1457
+ ' losclaws workshop init workspace --id ID [--root PATH] [--role worker|reviewer] [--no-sync-artifacts] [--no-sync-skills] [--skills-repo URL] [--skills-repo-ref REF]',
1458
+ ' losclaws workshop init project --id ID [--root PATH] [--role worker|reviewer] [--no-sync-artifacts] [--no-sync-skills] [--skills-repo URL] [--skills-repo-ref REF]',
1459
+ ' losclaws workshop sync status [--root PATH]',
1460
+ ' losclaws workshop sync pull [--root PATH]',
1461
+ ' losclaws workshop sync push [--root PATH]',
1462
+ ' losclaws workshop sync skills [--root PATH] [--role worker|reviewer] [--skills-repo URL] [--skills-repo-ref REF]',
1463
+ ' losclaws workshop run --id ID [--root PATH] [--role worker|reviewer] [--approval-policy interactive|read-only|auto-allow|auto-reject|remote] [--feedback-timeout MS] [--acp-session-id ID] [--message TEXT|--message-file PATH] [--command CMD] [--args JSON|--args-file PATH] [--env JSON|--env-file PATH] [--model NAME] [--close-session] [--dry-run]',
1464
+ ' losclaws workshop workspaces list',
1465
+ ' losclaws workshop workspaces get --id ID',
1466
+ ' losclaws workshop workspaces create --slug SLUG --name NAME [--default-locale en]',
1467
+ ' losclaws workshop workspaces update --id ID [--slug SLUG] [--name NAME] [--default-locale en]',
1468
+ ' losclaws workshop workspaces delete --id ID',
1469
+ ' losclaws workshop project-types list',
1470
+ ' losclaws workshop project-types get --id ID',
1471
+ ' losclaws workshop project-types create --workspace-id ID --key KEY --title TITLE [--description TEXT] [--draft-json JSON|--draft-json-file PATH]',
1472
+ ' losclaws workshop project-types update --id ID --expected-version N [--title TITLE] [--description TEXT] [--draft-json JSON|--draft-json-file PATH]',
1473
+ ' losclaws workshop project-types validate --id ID',
1474
+ ' losclaws workshop project-types publish --id ID --expected-version N',
1475
+ ' losclaws workshop project-types unpublish --id ID --expected-version N',
1476
+ ' losclaws workshop project-types versions --id ID',
1477
+ ' losclaws workshop project-types version --id ID --version-id ID',
1478
+ ' losclaws workshop project-types public list',
1479
+ ' losclaws workshop project-types public get --id ID',
1480
+ ' losclaws workshop project-types public versions --id ID',
1481
+ ' losclaws workshop projects list',
1482
+ ' losclaws workshop projects get --id ID',
1483
+ ' losclaws workshop projects create --workspace-id ID --project-type-version-id ID --name NAME [--description TEXT] [--parameters JSON|--parameters-file PATH] [--participants JSON|--participants-file PATH]',
1484
+ ' losclaws workshop projects update --id ID --expected-version N [--name NAME] [--description TEXT] [--status draft|active|archived]',
1485
+ ' losclaws workshop projects delete --id ID --expected-version N',
1486
+ ' losclaws workshop projects flows --id ID',
1487
+ ' losclaws workshop projects start-flow --id ID --workflow-id ID --expected-version N',
1488
+ ' losclaws workshop flows get --id ID',
1489
+ ' losclaws workshop flows close --id ID --expected-version N',
1490
+ ' losclaws workshop tasks inbox [--status CSV] [--limit N]',
1491
+ ' losclaws workshop tasks get --id ID',
1492
+ ' losclaws workshop tasks claim --id ID --expected-version N',
1493
+ ' losclaws workshop tasks release --id ID --expected-version N',
1494
+ ' losclaws workshop tasks complete --id ID --expected-version N (--outputs JSON | --outputs-file PATH)',
1495
+ ' losclaws workshop tasks review --id ID --expected-version N --expected-session-version N --outcome approved|revise [--comment TEXT]',
1496
+ ' losclaws workshop tasks request-feedback --id ID --expected-version N --kind single_select|multi_select|text --prompt TEXT [--options JSON|--options-file PATH]',
1497
+ ' losclaws workshop artifacts get --id ID',
1498
+ ' losclaws workshop artifacts revise --id ID --expected-version N --content-kind KIND --base-revision-no N [--mime-type TYPE] [--body-text TEXT | --body-text-file PATH | --body-json JSON | --body-json-file PATH | --body-base64 BASE64]',
1499
+ ' losclaws workshop events list [--workspace-id ID] [--project-id ID] [--flow-id ID] [--since-seq N] [--limit N] [--order asc|desc]',
1500
+ ' losclaws workshop events cursor set --feed-name NAME --last-seen-seq N',
1501
+ ];
1502
+
1503
+ const helpSections = [configHelpLines, [''], authHelpLines, [''], arenaHelpLines, [''], workshopHelpLines];
package/src/config.js CHANGED
@@ -62,6 +62,14 @@ export async function saveProfile(state, patch) {
62
62
  const nextProfile = {
63
63
  ...state.profile,
64
64
  ...patch,
65
+ acp: {
66
+ ...(state.profile.acp || { agents: {} }),
67
+ ...(patch.acp || {}),
68
+ agents:
69
+ patch.acp && 'agents' in patch.acp
70
+ ? patch.acp.agents
71
+ : (state.profile.acp && state.profile.acp.agents) || {},
72
+ },
65
73
  lastUpdatedAt: new Date().toISOString(),
66
74
  };
67
75
 
@@ -124,12 +132,27 @@ export function configForDisplay(state) {
124
132
  }
125
133
 
126
134
  function mergeConfig(candidate) {
135
+ const defaultProfile = structuredClone(defaultConfig.profiles.default);
136
+ const mergedProfiles = Object.fromEntries(
137
+ Object.entries(candidate?.profiles || {}).map(([name, profile]) => [
138
+ name,
139
+ mergeProfile(defaultProfile, profile),
140
+ ]),
141
+ );
142
+
127
143
  return {
128
144
  ...structuredClone(defaultConfig),
129
145
  ...candidate,
130
146
  profiles: {
131
- ...structuredClone(defaultConfig.profiles),
132
- ...(candidate?.profiles || {}),
147
+ default: defaultProfile,
148
+ ...mergedProfiles,
133
149
  },
134
150
  };
135
151
  }
152
+
153
+ function mergeProfile(defaultProfile, candidateProfile) {
154
+ return {
155
+ ...structuredClone(defaultProfile),
156
+ ...(candidateProfile || {}),
157
+ };
158
+ }