@myvillage/cli 1.5.0 → 1.6.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/package.json +8 -7
- package/src/agent-runtime/loop.js +9 -9
- package/src/agent-runtime/mcp-client.js +31 -338
- package/src/commands/agent-local.js +24 -7
- package/src/commands/bizreqs.js +1 -25
- package/src/commands/checkin.js +149 -0
- package/src/commands/discover.js +220 -0
- package/src/commands/log.js +59 -0
- package/src/commands/login.js +160 -72
- package/src/commands/logout.js +43 -6
- package/src/commands/soulprint.js +1379 -0
- package/src/commands/story.js +68 -0
- package/src/index.js +141 -0
- package/src/utils/agent-scaffolder.js +6 -6
- package/src/utils/ai.js +30 -0
- package/src/utils/api.js +32 -1
- package/src/utils/config.js +2 -0
- package/src/utils/formatters.js +85 -0
- package/src/utils/man-hooks.js +16 -0
- package/src/utils/soulprint-api.js +136 -0
- package/src/utils/soulprint-workspace.js +158 -0
- package/src/utils/village-resolver.js +104 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { isAuthenticated } from '../utils/auth.js';
|
|
4
|
+
import { brand } from '../utils/brand.js';
|
|
5
|
+
import { createPost } from '../utils/api.js';
|
|
6
|
+
import { resolveVillageContext } from '../utils/village-resolver.js';
|
|
7
|
+
|
|
8
|
+
export async function storyCommand(options) {
|
|
9
|
+
if (!isAuthenticated()) {
|
|
10
|
+
console.log(chalk.red(" \u2717 Authentication required. Run 'myvillage login' first."));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const ctx = await resolveVillageContext();
|
|
15
|
+
if (!ctx) return;
|
|
16
|
+
|
|
17
|
+
// Guided prompts
|
|
18
|
+
const answers = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
type: 'editor',
|
|
21
|
+
name: 'story',
|
|
22
|
+
message: 'What story, memory, or piece of wisdom would you like to share?',
|
|
23
|
+
validate: (v) => v.trim().length > 0 || 'Please share something.',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'input',
|
|
27
|
+
name: 'source',
|
|
28
|
+
message: 'Who is this from? (you, a family elder, a community member)',
|
|
29
|
+
default: 'me',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'input',
|
|
33
|
+
name: 'connection',
|
|
34
|
+
message: 'Is this connected to something the village is building? (optional)',
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
// Build tags
|
|
39
|
+
const tags = ['knowledge-contribution'];
|
|
40
|
+
const sourceName = answers.source.trim();
|
|
41
|
+
if (sourceName && sourceName.toLowerCase() !== 'me') {
|
|
42
|
+
tags.push(`source:${sourceName}`);
|
|
43
|
+
}
|
|
44
|
+
if (answers.connection?.trim()) {
|
|
45
|
+
tags.push(`theme:${answers.connection.trim()}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Format body with attribution
|
|
49
|
+
let body = answers.story.trim();
|
|
50
|
+
if (sourceName && sourceName.toLowerCase() !== 'me') {
|
|
51
|
+
body = `"${body}"\n\n\u2014 ${sourceName}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
await createPost({
|
|
56
|
+
communitySlug: ctx.community.slug,
|
|
57
|
+
postType: 'DISCUSSION',
|
|
58
|
+
title: 'Knowledge Contribution',
|
|
59
|
+
body,
|
|
60
|
+
tags,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log(brand.green(` \u2713 Story shared with ${ctx.village.name}`));
|
|
64
|
+
} catch (err) {
|
|
65
|
+
const msg = err.response?.data?.error || err.message;
|
|
66
|
+
console.log(chalk.red(` \u2717 Failed to share story: ${msg}`));
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.js
CHANGED
|
@@ -53,6 +53,22 @@ import {
|
|
|
53
53
|
bizreqsStatusCommand,
|
|
54
54
|
bizreqsImportCommand,
|
|
55
55
|
} from './commands/bizreqs.js';
|
|
56
|
+
import { checkinCommand } from './commands/checkin.js';
|
|
57
|
+
import { discoverCommand } from './commands/discover.js';
|
|
58
|
+
import { logCommand } from './commands/log.js';
|
|
59
|
+
import { storyCommand } from './commands/story.js';
|
|
60
|
+
import {
|
|
61
|
+
soulprintInitCommand,
|
|
62
|
+
soulprintIngestCommand,
|
|
63
|
+
soulprintDatasetListCommand,
|
|
64
|
+
soulprintDatasetPullCommand,
|
|
65
|
+
soulprintTrainCommand,
|
|
66
|
+
soulprintJobsCommand,
|
|
67
|
+
soulprintJobDetailCommand,
|
|
68
|
+
soulprintPushCommand,
|
|
69
|
+
soulprintPublishCommand,
|
|
70
|
+
soulprintModelsCommand,
|
|
71
|
+
} from './commands/soulprint.js';
|
|
56
72
|
|
|
57
73
|
const require = createRequire(import.meta.url);
|
|
58
74
|
const pkg = require('../package.json');
|
|
@@ -73,6 +89,7 @@ export function run() {
|
|
|
73
89
|
program
|
|
74
90
|
.command('login')
|
|
75
91
|
.description('Authenticate with MyVillageOS')
|
|
92
|
+
.option('--no-browser', 'Manual login for headless environments (servers, SSH, etc.)')
|
|
76
93
|
.action(loginCommand);
|
|
77
94
|
|
|
78
95
|
program
|
|
@@ -308,6 +325,30 @@ export function run() {
|
|
|
308
325
|
.description('Remove an MCP server tool from a local agent')
|
|
309
326
|
.action(agentRemoveToolCommand);
|
|
310
327
|
|
|
328
|
+
// ── Village Content Pipeline ───────────────────────────
|
|
329
|
+
|
|
330
|
+
program
|
|
331
|
+
.command('checkin')
|
|
332
|
+
.description('Check in with your village and share updates')
|
|
333
|
+
.option('--skip-discover', 'Skip the auto-discover step')
|
|
334
|
+
.action(checkinCommand);
|
|
335
|
+
|
|
336
|
+
program
|
|
337
|
+
.command('discover')
|
|
338
|
+
.description('Find connections between villages')
|
|
339
|
+
.action(discoverCommand);
|
|
340
|
+
|
|
341
|
+
program
|
|
342
|
+
.command('log [text]')
|
|
343
|
+
.description('Quick build log entry')
|
|
344
|
+
.option('-t, --track <track>', 'Build track (game, portal, agent, data, robot, general)')
|
|
345
|
+
.action(logCommand);
|
|
346
|
+
|
|
347
|
+
program
|
|
348
|
+
.command('story')
|
|
349
|
+
.description('Share a story or wisdom with the network')
|
|
350
|
+
.action(storyCommand);
|
|
351
|
+
|
|
311
352
|
// ── BizReqs: Business Requirements Pipeline ───────────
|
|
312
353
|
|
|
313
354
|
const bizreqsCmd = program
|
|
@@ -357,5 +398,105 @@ export function run() {
|
|
|
357
398
|
.option('--contact <name>', 'Contact name')
|
|
358
399
|
.action(bizreqsImportCommand);
|
|
359
400
|
|
|
401
|
+
// ── SoulPrint Studio: Model Training Pipeline ───────────
|
|
402
|
+
|
|
403
|
+
const soulprintCmd = program
|
|
404
|
+
.command('soulprint')
|
|
405
|
+
.description('SoulPrint Studio \u2014 datasets, training, and model publishing');
|
|
406
|
+
|
|
407
|
+
soulprintCmd
|
|
408
|
+
.command('init')
|
|
409
|
+
.description('Initialize local training workspace')
|
|
410
|
+
.option('--skip-python', 'Skip Python venv setup')
|
|
411
|
+
.option('--skip-scripts', 'Skip training script download')
|
|
412
|
+
.action(soulprintInitCommand);
|
|
413
|
+
|
|
414
|
+
soulprintCmd
|
|
415
|
+
.command('ingest <path>')
|
|
416
|
+
.description('Ingest training data into a dataset')
|
|
417
|
+
.requiredOption('--dataset <slug>', 'Target dataset slug')
|
|
418
|
+
.requiredOption('--type <type>', 'Data type: text, image, audio, structured, multimodal')
|
|
419
|
+
.option('--source <source>', 'Ingestion source label', 'CLI')
|
|
420
|
+
.option('--captions <path>', 'Path to captions file (image/multimodal)')
|
|
421
|
+
.option('--transcriptions <path>', 'Path to transcriptions file (audio)')
|
|
422
|
+
.option('--schema <path>', 'Path to schema JSON file (structured)')
|
|
423
|
+
.option('--recursive', 'Recurse into subdirectories')
|
|
424
|
+
.option('--glob <pattern>', 'File glob pattern to filter')
|
|
425
|
+
.option('--split <split>', 'Assign all items to a split: train, validation, test')
|
|
426
|
+
.option('--dry-run', 'Show what would be ingested without uploading')
|
|
427
|
+
.option('--concurrency <n>', 'Parallel upload limit', '5')
|
|
428
|
+
.action(soulprintIngestCommand);
|
|
429
|
+
|
|
430
|
+
const soulprintDatasetCmd = soulprintCmd
|
|
431
|
+
.command('datasets')
|
|
432
|
+
.description('Manage training datasets');
|
|
433
|
+
|
|
434
|
+
soulprintDatasetCmd
|
|
435
|
+
.command('list')
|
|
436
|
+
.description('List available datasets on SoulPrint Studio')
|
|
437
|
+
.option('--type <type>', 'Filter by type: text, image, audio, structured, multimodal')
|
|
438
|
+
.option('--status <status>', 'Filter by status: collecting, ready, training, archived')
|
|
439
|
+
.option('--json', 'Output raw JSON')
|
|
440
|
+
.action(soulprintDatasetListCommand);
|
|
441
|
+
|
|
442
|
+
soulprintDatasetCmd
|
|
443
|
+
.command('pull <slug>')
|
|
444
|
+
.description('Download a dataset to your local machine')
|
|
445
|
+
.option('--version <number>', 'Specific version (default: latest frozen)')
|
|
446
|
+
.option('--split <split>', 'Download only a specific split: train, validation, test')
|
|
447
|
+
.option('--force', 'Re-download even if already present locally')
|
|
448
|
+
.action(soulprintDatasetPullCommand);
|
|
449
|
+
|
|
450
|
+
soulprintCmd
|
|
451
|
+
.command('train')
|
|
452
|
+
.description('Run model training locally')
|
|
453
|
+
.requiredOption('--type <type>', 'Training type: text, image, audio, structured, multimodal')
|
|
454
|
+
.requiredOption('--dataset <slug>', 'Dataset slug to train on')
|
|
455
|
+
.option('--version <number>', 'Dataset version (default: latest)')
|
|
456
|
+
.option('--base <model>', 'Base model identifier')
|
|
457
|
+
.option('--method <method>', 'Training method')
|
|
458
|
+
.option('--config <path>', 'Path to training config YAML file')
|
|
459
|
+
.option('--name <name>', 'Name for the resulting model')
|
|
460
|
+
.option('--dry-run', 'Validate config and dataset without starting training')
|
|
461
|
+
.action(soulprintTrainCommand);
|
|
462
|
+
|
|
463
|
+
soulprintCmd
|
|
464
|
+
.command('jobs [jobId]')
|
|
465
|
+
.description('View training job status')
|
|
466
|
+
.option('--status <status>', 'Filter by status')
|
|
467
|
+
.option('--json', 'Output raw JSON')
|
|
468
|
+
.action((jobId, options) => {
|
|
469
|
+
if (jobId) return soulprintJobDetailCommand(jobId, options);
|
|
470
|
+
return soulprintJobsCommand(options);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
soulprintCmd
|
|
474
|
+
.command('models')
|
|
475
|
+
.description('List models in the registry')
|
|
476
|
+
.option('--status <status>', 'Filter: draft, validated, published, rejected')
|
|
477
|
+
.option('--type <type>', 'Filter: text, image, audio, structured, multimodal')
|
|
478
|
+
.option('--json', 'Output raw JSON')
|
|
479
|
+
.action(soulprintModelsCommand);
|
|
480
|
+
|
|
481
|
+
soulprintCmd
|
|
482
|
+
.command('push <path>')
|
|
483
|
+
.description('Upload locally trained model artifacts to SoulPrint Studio')
|
|
484
|
+
.requiredOption('--name <name>', 'Model name')
|
|
485
|
+
.requiredOption('--type <type>', 'Model type: text, image, audio, structured, multimodal')
|
|
486
|
+
.option('--base <model>', 'Base model it was trained from')
|
|
487
|
+
.option('--method <method>', 'Training method used')
|
|
488
|
+
.option('--job <jobId>', 'Link to an existing training job')
|
|
489
|
+
.option('--description <text>', 'Model description')
|
|
490
|
+
.action(soulprintPushCommand);
|
|
491
|
+
|
|
492
|
+
soulprintCmd
|
|
493
|
+
.command('publish <modelSlug>')
|
|
494
|
+
.description('Publish a validated model to the MyVillage platform')
|
|
495
|
+
.option('--villager <id>', 'Target villager ID to own the model')
|
|
496
|
+
.option('--villages <ids>', 'Comma-separated village IDs to associate')
|
|
497
|
+
.option('--tier <tier>', 'Model tier: FREE, BASIC, PRO, ENTERPRISE', 'FREE')
|
|
498
|
+
.option('--public', 'Make the model publicly available')
|
|
499
|
+
.action(soulprintPublishCommand);
|
|
500
|
+
|
|
360
501
|
program.parse();
|
|
361
502
|
}
|
|
@@ -5,9 +5,9 @@ import { stringify as stringifyYaml } from 'yaml';
|
|
|
5
5
|
// ── MCP Tool Catalog ────────────────────────────────────
|
|
6
6
|
|
|
7
7
|
const TOOL_CATALOG = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
description: '
|
|
8
|
+
myvillage: {
|
|
9
|
+
url: 'https://mcp.myvillageproject.ai',
|
|
10
|
+
description: 'MyVillageOS platform access (feed, posts, communities, wallet, knowledge)',
|
|
11
11
|
always_enabled: true,
|
|
12
12
|
},
|
|
13
13
|
filesystem: {
|
|
@@ -151,11 +151,11 @@ ${description}
|
|
|
151
151
|
function generateToolsYaml(selectedTools) {
|
|
152
152
|
const servers = {};
|
|
153
153
|
|
|
154
|
-
//
|
|
155
|
-
servers['
|
|
154
|
+
// myvillage is always included
|
|
155
|
+
servers['myvillage'] = TOOL_CATALOG['myvillage'];
|
|
156
156
|
|
|
157
157
|
for (const toolId of selectedTools) {
|
|
158
|
-
if (toolId === '
|
|
158
|
+
if (toolId === 'myvillage') continue;
|
|
159
159
|
if (TOOL_CATALOG[toolId]) {
|
|
160
160
|
servers[toolId] = { ...TOOL_CATALOG[toolId] };
|
|
161
161
|
}
|
package/src/utils/ai.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { getConfig } from './config.js';
|
|
4
|
+
import { brand } from './brand.js';
|
|
5
|
+
|
|
6
|
+
export async function getAnthropicProvider() {
|
|
7
|
+
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
8
|
+
const config = getConfig();
|
|
9
|
+
let apiKey = config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
10
|
+
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
console.log(chalk.yellow('\n Anthropic API key required for AI features.'));
|
|
13
|
+
console.log(brand.teal(' Get yours at: https://console.anthropic.com\n'));
|
|
14
|
+
|
|
15
|
+
const { key } = await inquirer.prompt([{
|
|
16
|
+
type: 'password',
|
|
17
|
+
name: 'key',
|
|
18
|
+
message: 'Anthropic API key:',
|
|
19
|
+
mask: '*',
|
|
20
|
+
validate: (input) => input.trim().length > 0 || 'API key is required',
|
|
21
|
+
}]);
|
|
22
|
+
|
|
23
|
+
apiKey = key.trim();
|
|
24
|
+
const { setConfig } = await import('./config.js');
|
|
25
|
+
setConfig({ anthropicApiKey: apiKey });
|
|
26
|
+
console.log(brand.green(' \u2713 API key saved to ~/.myvillage/config.json\n'));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return createAnthropic({ apiKey });
|
|
30
|
+
}
|
package/src/utils/api.js
CHANGED
|
@@ -7,7 +7,7 @@ const require = createRequire(import.meta.url);
|
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
8
|
const USER_AGENT = `MyVillageOS-CLI/${version}`;
|
|
9
9
|
|
|
10
|
-
function createClient(baseURL) {
|
|
10
|
+
export function createClient(baseURL) {
|
|
11
11
|
const client = axios.create({
|
|
12
12
|
baseURL,
|
|
13
13
|
headers: {
|
|
@@ -379,6 +379,37 @@ export async function registerOAuthClient(name, appType, redirectUris) {
|
|
|
379
379
|
return response.data;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
// ── Platform API Client (/api) ──────────────────────────
|
|
383
|
+
|
|
384
|
+
export function getPlatformClient() {
|
|
385
|
+
const config = getConfig();
|
|
386
|
+
return createClient(config.platformBaseUrl);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export async function getVillagerVillages(villagerUuid) {
|
|
390
|
+
const client = getPlatformClient();
|
|
391
|
+
const response = await client.get(`/villagers/${villagerUuid}/villages`);
|
|
392
|
+
return response.data;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export async function getVillagerByVillagerId(villagerId) {
|
|
396
|
+
const client = getPlatformClient();
|
|
397
|
+
const response = await client.get(`/villagers/by-villager-id/${villagerId}`);
|
|
398
|
+
return response.data;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export async function getVillageCommunities(villageUuid) {
|
|
402
|
+
const client = getNetworkClient();
|
|
403
|
+
const response = await client.get('/communities', { params: { villageId: villageUuid } });
|
|
404
|
+
return response.data;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export async function listPostsByFilters(params = {}) {
|
|
408
|
+
const client = getNetworkClient();
|
|
409
|
+
const response = await client.get('/posts', { params });
|
|
410
|
+
return response.data;
|
|
411
|
+
}
|
|
412
|
+
|
|
382
413
|
// ── BizReqs API Client (/api/bizreqs) ───────────────────
|
|
383
414
|
|
|
384
415
|
export function getBizReqsClient() {
|
package/src/utils/config.js
CHANGED
|
@@ -10,6 +10,8 @@ const DEFAULT_CONFIG = {
|
|
|
10
10
|
networkBaseUrl: 'https://portal.myvillageproject.ai/api/network',
|
|
11
11
|
bizreqsBaseUrl: 'https://portal.myvillageproject.ai/api/bizreqs',
|
|
12
12
|
oauthBaseUrl: 'https://portal.myvillageproject.ai/api/oauth',
|
|
13
|
+
soulprintBaseUrl: 'https://soulprint-studio.myvillageproject.ai/api',
|
|
14
|
+
platformBaseUrl: 'https://portal.myvillageproject.ai/api',
|
|
13
15
|
clientId: 'mvos_aG_c729fuQxvvqYHOnkgTQ',
|
|
14
16
|
callbackPort: 3737,
|
|
15
17
|
anthropicApiKey: null,
|
package/src/utils/formatters.js
CHANGED
|
@@ -743,3 +743,88 @@ export function formatPagination(meta) {
|
|
|
743
743
|
console.log(chalk.dim(` ── More results available. Run with --cursor=${meta.nextCursor} to see next page\n`));
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
|
+
|
|
747
|
+
// ── MAN Content Pipeline ────────────────────────────────
|
|
748
|
+
|
|
749
|
+
export function formatCheckinSummary(post, meta) {
|
|
750
|
+
const b = (s) => chalk.hex('#B07C00')(s);
|
|
751
|
+
const width = 56;
|
|
752
|
+
const bdr = '\u2500'.repeat(width);
|
|
753
|
+
|
|
754
|
+
const lines = [''];
|
|
755
|
+
lines.push(` ${b('\u250C' + bdr + '\u2510')}`);
|
|
756
|
+
lines.push(` ${b('\u2502')} ${brand.gold('\u2743 Village Check-in')}${' '.repeat(width - 19)}${b('\u2502')}`);
|
|
757
|
+
lines.push(` ${b('\u2502' + '\u2500'.repeat(width) + '\u2502')}`);
|
|
758
|
+
|
|
759
|
+
if (meta.villageName) {
|
|
760
|
+
const vl = ` Village: ${meta.villageName}`;
|
|
761
|
+
lines.push(` ${b('\u2502')}${brand.cream(vl).padEnd(width + 12)}${b('\u2502')}`);
|
|
762
|
+
}
|
|
763
|
+
if (meta.buildTrack) {
|
|
764
|
+
const tl = ` Track: ${meta.buildTrack}`;
|
|
765
|
+
lines.push(` ${b('\u2502')}${brand.teal(tl).padEnd(width + 12)}${b('\u2502')}`);
|
|
766
|
+
}
|
|
767
|
+
if (meta.themes?.length) {
|
|
768
|
+
const th = ` Themes: ${meta.themes.join(', ')}`;
|
|
769
|
+
lines.push(` ${b('\u2502')}${chalk.white(th).padEnd(width + 12)}${b('\u2502')}`);
|
|
770
|
+
}
|
|
771
|
+
if (meta.needs?.length) {
|
|
772
|
+
const nd = ` Needs: ${meta.needs.join(', ')}`;
|
|
773
|
+
lines.push(` ${b('\u2502')}${chalk.yellow(nd).padEnd(width + 12)}${b('\u2502')}`);
|
|
774
|
+
}
|
|
775
|
+
if (meta.offers?.length) {
|
|
776
|
+
const of_ = ` Offers: ${meta.offers.join(', ')}`;
|
|
777
|
+
lines.push(` ${b('\u2502')}${brand.green(of_).padEnd(width + 12)}${b('\u2502')}`);
|
|
778
|
+
}
|
|
779
|
+
if (post?.data?.id) {
|
|
780
|
+
const id = ` Post ID: ${post.data.id.slice(0, 8)}...`;
|
|
781
|
+
lines.push(` ${b('\u2502')}${chalk.dim(id).padEnd(width + 12)}${b('\u2502')}`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
lines.push(` ${b('\u2514' + bdr + '\u2518')}`);
|
|
785
|
+
lines.push('');
|
|
786
|
+
|
|
787
|
+
console.log(lines.join('\n'));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export function formatConnection(connection) {
|
|
791
|
+
const b = (s) => chalk.hex('#B07C00')(s);
|
|
792
|
+
const width = 56;
|
|
793
|
+
const bdr = '\u2500'.repeat(width);
|
|
794
|
+
|
|
795
|
+
const lines = [''];
|
|
796
|
+
lines.push(` ${b('\u250C' + bdr + '\u2510')}`);
|
|
797
|
+
lines.push(` ${b('\u2502')} ${brand.gold('\u21C4 Connection Found')}${' '.repeat(width - 20)}${b('\u2502')}`);
|
|
798
|
+
lines.push(` ${b('\u2502' + '\u2500'.repeat(width) + '\u2502')}`);
|
|
799
|
+
|
|
800
|
+
if (connection.villageName) {
|
|
801
|
+
const vl = ` Village: ${connection.villageName}`;
|
|
802
|
+
lines.push(` ${b('\u2502')}${brand.cream(vl).padEnd(width + 12)}${b('\u2502')}`);
|
|
803
|
+
}
|
|
804
|
+
if (connection.topic) {
|
|
805
|
+
const tp = ` Topic: ${connection.topic}`;
|
|
806
|
+
lines.push(` ${b('\u2502')}${chalk.white(tp).padEnd(width + 12)}${b('\u2502')}`);
|
|
807
|
+
}
|
|
808
|
+
if (connection.matchType) {
|
|
809
|
+
const mt = ` Match: ${connection.matchType}`;
|
|
810
|
+
lines.push(` ${b('\u2502')}${brand.teal(mt).padEnd(width + 12)}${b('\u2502')}`);
|
|
811
|
+
}
|
|
812
|
+
if (connection.score !== undefined) {
|
|
813
|
+
const sc = ` Relevance: ${'\u2605'.repeat(Math.min(connection.score, 5))}${'\u2606'.repeat(Math.max(0, 5 - connection.score))}`;
|
|
814
|
+
lines.push(` ${b('\u2502')}${brand.gold(sc).padEnd(width + 12)}${b('\u2502')}`);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
lines.push(` ${b('\u2514' + bdr + '\u2518')}`);
|
|
818
|
+
lines.push('');
|
|
819
|
+
|
|
820
|
+
console.log(lines.join('\n'));
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function formatBuildLog(post) {
|
|
824
|
+
const tags = post.tags || [];
|
|
825
|
+
const track = tags.find((t) => t.startsWith('track:'))?.replace('track:', '') || 'general';
|
|
826
|
+
const body = post.body?.length > 60 ? post.body.slice(0, 57) + '...' : post.body || '';
|
|
827
|
+
const time = relativeTime(post.createdAt);
|
|
828
|
+
|
|
829
|
+
console.log(` ${brand.teal(`[${track}]`)} ${chalk.white(body)} ${chalk.dim(time)}`);
|
|
830
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trigger a MAN post from an automated event (deploy, publish, etc.).
|
|
5
|
+
* Stub implementation — future commands will call this to auto-generate MAN posts.
|
|
6
|
+
*
|
|
7
|
+
* @param {object} params
|
|
8
|
+
* @param {string} params.trigger - What triggered this (e.g., 'deploy', 'publish')
|
|
9
|
+
* @param {string} params.artifactId - ID of the artifact (game ID, model ID, etc.)
|
|
10
|
+
* @param {string} params.summary - Human-readable summary of what happened
|
|
11
|
+
* @param {string} params.villageId - Village UUID
|
|
12
|
+
* @param {string} params.communitySlug - Target community slug
|
|
13
|
+
*/
|
|
14
|
+
export async function triggerMANPost({ trigger, artifactId, summary, villageId, communitySlug }) {
|
|
15
|
+
console.log(chalk.dim(` [MAN hook] ${trigger}: ${summary} (not yet wired — future feature)`));
|
|
16
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createClient } from './api.js';
|
|
2
|
+
import { getConfig } from './config.js';
|
|
3
|
+
|
|
4
|
+
// ── Client Factory ─────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export function getSoulprintClient() {
|
|
7
|
+
const config = getConfig();
|
|
8
|
+
return createClient(config.soulprintBaseUrl);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// ── Datasets ───────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export async function listDatasets(params = {}) {
|
|
14
|
+
const client = getSoulprintClient();
|
|
15
|
+
const response = await client.get('/datasets', { params });
|
|
16
|
+
return response.data;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function getDataset(slug) {
|
|
20
|
+
const client = getSoulprintClient();
|
|
21
|
+
const response = await client.get(`/datasets/${slug}`);
|
|
22
|
+
return response.data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function getDatasetDownload(slug, version) {
|
|
26
|
+
const client = getSoulprintClient();
|
|
27
|
+
const params = version ? { version } : {};
|
|
28
|
+
const response = await client.get(`/datasets/${slug}/download`, { params });
|
|
29
|
+
return response.data;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ── Training Jobs ──────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
export async function createJob(data) {
|
|
35
|
+
const client = getSoulprintClient();
|
|
36
|
+
const response = await client.post('/jobs', data);
|
|
37
|
+
return response.data;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function listJobs(params = {}) {
|
|
41
|
+
const client = getSoulprintClient();
|
|
42
|
+
const response = await client.get('/jobs', { params });
|
|
43
|
+
return response.data;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function getJob(id) {
|
|
47
|
+
const client = getSoulprintClient();
|
|
48
|
+
const response = await client.get(`/jobs/${id}`);
|
|
49
|
+
return response.data;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function updateJobStatus(id, data) {
|
|
53
|
+
const client = getSoulprintClient();
|
|
54
|
+
const response = await client.patch(`/jobs/${id}/status`, data);
|
|
55
|
+
return response.data;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function completeJob(id, data) {
|
|
59
|
+
const client = getSoulprintClient();
|
|
60
|
+
const response = await client.post(`/jobs/${id}/complete`, data);
|
|
61
|
+
return response.data;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function failJob(id, data) {
|
|
65
|
+
const client = getSoulprintClient();
|
|
66
|
+
const response = await client.post(`/jobs/${id}/fail`, data);
|
|
67
|
+
return response.data;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Models ─────────────────────────────────────────────
|
|
71
|
+
|
|
72
|
+
export async function listModels(params = {}) {
|
|
73
|
+
const client = getSoulprintClient();
|
|
74
|
+
const response = await client.get('/models', { params });
|
|
75
|
+
return response.data;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function getModel(slug) {
|
|
79
|
+
const client = getSoulprintClient();
|
|
80
|
+
const response = await client.get(`/models/${slug}`);
|
|
81
|
+
return response.data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function publishModel(slug, data) {
|
|
85
|
+
const client = getSoulprintClient();
|
|
86
|
+
const response = await client.post(`/models/${slug}/publish`, data);
|
|
87
|
+
return response.data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Ingestion ──────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
export async function ingestText(data) {
|
|
93
|
+
const client = getSoulprintClient();
|
|
94
|
+
const response = await client.post('/ingest/text', data);
|
|
95
|
+
return response.data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function ingestStructured(data) {
|
|
99
|
+
const client = getSoulprintClient();
|
|
100
|
+
const response = await client.post('/ingest/structured', data);
|
|
101
|
+
return response.data;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function prepareIngestion(data) {
|
|
105
|
+
const client = getSoulprintClient();
|
|
106
|
+
const response = await client.post('/ingest/prepare', data);
|
|
107
|
+
return response.data;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function completeIngestion(ingestionId, data) {
|
|
111
|
+
const client = getSoulprintClient();
|
|
112
|
+
const response = await client.post(`/ingest/${ingestionId}/complete`, data);
|
|
113
|
+
return response.data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function abortIngestion(ingestionId) {
|
|
117
|
+
const client = getSoulprintClient();
|
|
118
|
+
const response = await client.post(`/ingest/${ingestionId}/abort`);
|
|
119
|
+
return response.data;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Upload ─────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
export async function getUploadUrls(data) {
|
|
125
|
+
const client = getSoulprintClient();
|
|
126
|
+
const response = await client.post('/upload/presign', data);
|
|
127
|
+
return response.data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Scripts ────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
export async function getScriptsManifest() {
|
|
133
|
+
const client = getSoulprintClient();
|
|
134
|
+
const response = await client.get('/scripts/manifest');
|
|
135
|
+
return response.data;
|
|
136
|
+
}
|