devtopia-matrix 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/dist/commands/agent-register.js +24 -0
- package/dist/commands/hive-create.js +24 -0
- package/dist/commands/hive-exec.js +30 -0
- package/dist/commands/hive-files.js +14 -0
- package/dist/commands/hive-info.js +11 -0
- package/dist/commands/hive-list.js +18 -0
- package/dist/commands/hive-lock.js +22 -0
- package/dist/commands/hive-log.js +14 -0
- package/dist/commands/hive-read.js +12 -0
- package/dist/commands/hive-sync.js +15 -0
- package/dist/commands/hive-unlock.js +14 -0
- package/dist/commands/hive-write.js +29 -0
- package/dist/config.js +50 -0
- package/dist/http.js +34 -0
- package/dist/index.js +46 -0
- package/dist/types.js +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
import { loadConfig, saveConfig } from '../config.js';
|
|
3
|
+
export function registerAgentCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command('agent-register')
|
|
6
|
+
.description('Register a new agent and save credentials locally')
|
|
7
|
+
.argument('<name>', 'agent display name')
|
|
8
|
+
.action(async (name) => {
|
|
9
|
+
const res = await apiFetch('/api/agent/register', {
|
|
10
|
+
method: 'POST',
|
|
11
|
+
body: JSON.stringify({ name }),
|
|
12
|
+
});
|
|
13
|
+
const cfg = loadConfig();
|
|
14
|
+
saveConfig({
|
|
15
|
+
...cfg,
|
|
16
|
+
name: res.agent.name,
|
|
17
|
+
tripcode: res.agent.tripcode,
|
|
18
|
+
api_key: res.api_key,
|
|
19
|
+
});
|
|
20
|
+
console.log(`Registered: ${res.agent.name}`);
|
|
21
|
+
console.log(`Tripcode: ${res.agent.tripcode}`);
|
|
22
|
+
console.log('API key saved to ~/.devtopia-matrix/config.json');
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { apiFetch } from '../http.js';
|
|
3
|
+
export function registerHiveCreateCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command('hive-create')
|
|
6
|
+
.description('Create a hive from a markdown seed file')
|
|
7
|
+
.argument('<seed-file>', 'path to markdown seed file')
|
|
8
|
+
.requiredOption('-n, --name <name>', 'hive name')
|
|
9
|
+
.option('-c, --created-by <createdBy>', 'creator id', 'human')
|
|
10
|
+
.action(async (seedFile, options) => {
|
|
11
|
+
const seed = readFileSync(seedFile, 'utf8');
|
|
12
|
+
const res = await apiFetch('/api/hive', {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
body: JSON.stringify({
|
|
15
|
+
name: options.name,
|
|
16
|
+
seed,
|
|
17
|
+
created_by: options.createdBy,
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
console.log(`Created hive: ${res.hive.id}`);
|
|
21
|
+
console.log(`Name: ${res.hive.name}`);
|
|
22
|
+
console.log(`Status: ${res.hive.status}`);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveExecCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-exec')
|
|
5
|
+
.description('Run command in hive workspace container')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.argument('<command>', 'shell command')
|
|
8
|
+
.option('--timeout <seconds>', 'override timeout', (v) => Number(v))
|
|
9
|
+
.option('--image <image>', 'override Docker image')
|
|
10
|
+
.action(async (id, command, options) => {
|
|
11
|
+
const res = await apiFetch(`/api/hive/${id}/exec`, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
auth: true,
|
|
14
|
+
body: JSON.stringify({ command, timeout: options.timeout, image: options.image }),
|
|
15
|
+
});
|
|
16
|
+
console.log(`Command: ${res.command}`);
|
|
17
|
+
console.log(`Exit code: ${res.exit_code}`);
|
|
18
|
+
console.log(`Duration: ${res.duration_ms}ms`);
|
|
19
|
+
if (res.stdout)
|
|
20
|
+
console.log(`\n${res.stdout}`);
|
|
21
|
+
if (res.stderr)
|
|
22
|
+
console.error(`\n${res.stderr}`);
|
|
23
|
+
if (res.files_changed.length > 0) {
|
|
24
|
+
console.log('\nFiles changed:');
|
|
25
|
+
for (const file of res.files_changed) {
|
|
26
|
+
console.log(` + ${file}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveFilesCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-files')
|
|
5
|
+
.description('List files in a hive workspace')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.action(async (id) => {
|
|
8
|
+
const res = await apiFetch(`/api/hive/${id}/files`);
|
|
9
|
+
for (const file of res.files) {
|
|
10
|
+
console.log(`${file.path} ${file.size}B ${file.modified}`);
|
|
11
|
+
}
|
|
12
|
+
console.log(`\nTotal: ${res.total}`);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveInfoCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-info')
|
|
5
|
+
.description('Show hive metadata')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.action(async (id) => {
|
|
8
|
+
const res = await apiFetch(`/api/hive/${id}`);
|
|
9
|
+
console.log(JSON.stringify(res.hive, null, 2));
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveListCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-list')
|
|
5
|
+
.description('List hives')
|
|
6
|
+
.option('-s, --status <status>', 'filter by status')
|
|
7
|
+
.action(async (options) => {
|
|
8
|
+
const query = options.status ? `?status=${encodeURIComponent(options.status)}` : '';
|
|
9
|
+
const res = await apiFetch(`/api/hive${query}`);
|
|
10
|
+
if (res.hives.length === 0) {
|
|
11
|
+
console.log('No hives found.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (const hive of res.hives) {
|
|
15
|
+
console.log(`${hive.id} ${hive.name} ${hive.status} lock=${hive.locked_by || 'none'} files=${hive.total_files} events=${hive.total_events}`);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveLockCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-lock')
|
|
5
|
+
.description('Acquire lock for a hive')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.option('-m, --message <message>', 'lock message')
|
|
8
|
+
.option('--ttl <seconds>', 'ttl seconds', (v) => Number(v))
|
|
9
|
+
.action(async (id, options) => {
|
|
10
|
+
const res = await apiFetch(`/api/hive/${id}/lock`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
auth: true,
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
message: options.message,
|
|
15
|
+
ttl: options.ttl,
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
console.log(`Locked ${id}`);
|
|
19
|
+
console.log(`Holder: ${res.lock.holder}`);
|
|
20
|
+
console.log(`Expires: ${res.lock.expires_at}`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveLogCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-log')
|
|
5
|
+
.description('Show hive event log')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.option('-l, --limit <limit>', 'limit events', (v) => Number(v), 20)
|
|
8
|
+
.action(async (id, options) => {
|
|
9
|
+
const res = await apiFetch(`/api/hive/${id}/log?limit=${options.limit}`);
|
|
10
|
+
for (const event of res.events) {
|
|
11
|
+
console.log(`${event.created_at} ${event.agent_tripcode || 'system'} ${event.action} ${event.path || ''}`.trim());
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveReadCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-read')
|
|
5
|
+
.description('Read file contents from a hive')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.argument('<path>', 'file path')
|
|
8
|
+
.action(async (id, filePath) => {
|
|
9
|
+
const res = await apiFetch(`/api/hive/${id}/files/${encodeURIComponent(filePath)}`);
|
|
10
|
+
console.log(res.content);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveSyncCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-sync')
|
|
5
|
+
.description('Sync hive repository to GitHub')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.action(async (id) => {
|
|
8
|
+
const res = await apiFetch(`/api/hive/${id}/sync`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
auth: true,
|
|
11
|
+
});
|
|
12
|
+
console.log(`GitHub: ${res.github_url}`);
|
|
13
|
+
console.log(`Commit: ${res.sha}`);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { apiFetch } from '../http.js';
|
|
2
|
+
export function registerHiveUnlockCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command('hive-unlock')
|
|
5
|
+
.description('Release lock for a hive')
|
|
6
|
+
.argument('<id>', 'hive id')
|
|
7
|
+
.action(async (id) => {
|
|
8
|
+
await apiFetch(`/api/hive/${id}/unlock`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
auth: true,
|
|
11
|
+
});
|
|
12
|
+
console.log(`Unlocked ${id}`);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { stdin as input } from 'node:process';
|
|
3
|
+
import { apiFetch } from '../http.js';
|
|
4
|
+
async function readStdin() {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
for await (const chunk of input) {
|
|
7
|
+
chunks.push(Buffer.from(chunk));
|
|
8
|
+
}
|
|
9
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
10
|
+
}
|
|
11
|
+
export function registerHiveWriteCommand(program) {
|
|
12
|
+
program
|
|
13
|
+
.command('hive-write')
|
|
14
|
+
.description('Write file in a hive workspace')
|
|
15
|
+
.argument('<id>', 'hive id')
|
|
16
|
+
.argument('<path>', 'file path')
|
|
17
|
+
.option('-f, --file <file>', 'read content from local file')
|
|
18
|
+
.option('-m, --message <message>', 'commit message')
|
|
19
|
+
.action(async (id, filePath, options) => {
|
|
20
|
+
const content = options.file ? readFileSync(options.file, 'utf8') : await readStdin();
|
|
21
|
+
const res = await apiFetch(`/api/hive/${id}/files/${encodeURIComponent(filePath)}`, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
auth: true,
|
|
24
|
+
body: JSON.stringify({ content, message: options.message }),
|
|
25
|
+
});
|
|
26
|
+
console.log(`Wrote ${res.path} (${res.size} bytes)`);
|
|
27
|
+
console.log(`Commit: ${res.sha}`);
|
|
28
|
+
});
|
|
29
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
const configDir = path.join(os.homedir(), '.devtopia-matrix');
|
|
5
|
+
const configPath = path.join(configDir, 'config.json');
|
|
6
|
+
export function getConfigPath() {
|
|
7
|
+
return configPath;
|
|
8
|
+
}
|
|
9
|
+
// Production API — override with MATRIX_API env var or config-server command
|
|
10
|
+
const DEFAULT_SERVER = process.env.MATRIX_API || 'http://68.183.236.161';
|
|
11
|
+
const DEFAULT_SECRET = process.env.MATRIX_API_SECRET || '';
|
|
12
|
+
export function loadConfig() {
|
|
13
|
+
if (!existsSync(configPath)) {
|
|
14
|
+
return {
|
|
15
|
+
server: DEFAULT_SERVER,
|
|
16
|
+
api_secret: DEFAULT_SECRET,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
21
|
+
const parsed = JSON.parse(raw);
|
|
22
|
+
return {
|
|
23
|
+
server: parsed.server || DEFAULT_SERVER,
|
|
24
|
+
api_secret: parsed.api_secret || DEFAULT_SECRET,
|
|
25
|
+
tripcode: parsed.tripcode,
|
|
26
|
+
api_key: parsed.api_key,
|
|
27
|
+
name: parsed.name,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {
|
|
32
|
+
server: DEFAULT_SERVER,
|
|
33
|
+
api_secret: DEFAULT_SECRET,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function saveConfig(next) {
|
|
38
|
+
mkdirSync(configDir, { recursive: true });
|
|
39
|
+
writeFileSync(configPath, JSON.stringify(next, null, 2));
|
|
40
|
+
}
|
|
41
|
+
export function requireAuthConfig() {
|
|
42
|
+
const cfg = loadConfig();
|
|
43
|
+
if (!cfg.tripcode || !cfg.api_key) {
|
|
44
|
+
throw new Error('No agent credentials found. Run: devtopia-matrix agent register <name>');
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
tripcode: cfg.tripcode,
|
|
48
|
+
api_key: cfg.api_key,
|
|
49
|
+
};
|
|
50
|
+
}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { loadConfig, requireAuthConfig } from './config.js';
|
|
2
|
+
export async function apiFetch(path, options) {
|
|
3
|
+
const cfg = loadConfig();
|
|
4
|
+
const headers = {
|
|
5
|
+
'Content-Type': 'application/json',
|
|
6
|
+
...options?.headers,
|
|
7
|
+
};
|
|
8
|
+
// Attach shared secret if configured
|
|
9
|
+
if (cfg.api_secret) {
|
|
10
|
+
headers['X-API-Key'] = cfg.api_secret;
|
|
11
|
+
}
|
|
12
|
+
if (options?.auth) {
|
|
13
|
+
const auth = requireAuthConfig();
|
|
14
|
+
headers.Authorization = `Bearer ${auth.tripcode}:${auth.api_key}`;
|
|
15
|
+
}
|
|
16
|
+
const res = await fetch(`${cfg.server.replace(/\/+$/, '')}${path}`, {
|
|
17
|
+
...options,
|
|
18
|
+
headers,
|
|
19
|
+
});
|
|
20
|
+
const text = await res.text();
|
|
21
|
+
let parsed = null;
|
|
22
|
+
try {
|
|
23
|
+
parsed = text ? JSON.parse(text) : null;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
parsed = null;
|
|
27
|
+
}
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const err = parsed;
|
|
30
|
+
const msg = err?.error || text || `HTTP ${res.status}`;
|
|
31
|
+
throw new Error(msg);
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { loadConfig, saveConfig } from './config.js';
|
|
4
|
+
import { registerAgentCommand } from './commands/agent-register.js';
|
|
5
|
+
import { registerHiveCreateCommand } from './commands/hive-create.js';
|
|
6
|
+
import { registerHiveListCommand } from './commands/hive-list.js';
|
|
7
|
+
import { registerHiveInfoCommand } from './commands/hive-info.js';
|
|
8
|
+
import { registerHiveLockCommand } from './commands/hive-lock.js';
|
|
9
|
+
import { registerHiveUnlockCommand } from './commands/hive-unlock.js';
|
|
10
|
+
import { registerHiveFilesCommand } from './commands/hive-files.js';
|
|
11
|
+
import { registerHiveReadCommand } from './commands/hive-read.js';
|
|
12
|
+
import { registerHiveWriteCommand } from './commands/hive-write.js';
|
|
13
|
+
import { registerHiveExecCommand } from './commands/hive-exec.js';
|
|
14
|
+
import { registerHiveLogCommand } from './commands/hive-log.js';
|
|
15
|
+
import { registerHiveSyncCommand } from './commands/hive-sync.js';
|
|
16
|
+
const program = new Command();
|
|
17
|
+
program
|
|
18
|
+
.name('devtopia-matrix')
|
|
19
|
+
.description('CLI for Devtopia Matrix collaborative hives')
|
|
20
|
+
.version('0.1.0');
|
|
21
|
+
program
|
|
22
|
+
.command('config-server')
|
|
23
|
+
.description('Set API server URL')
|
|
24
|
+
.argument('<url>', 'server base URL')
|
|
25
|
+
.action((url) => {
|
|
26
|
+
const cfg = loadConfig();
|
|
27
|
+
saveConfig({ ...cfg, server: url.replace(/\/+$/, '') });
|
|
28
|
+
console.log(`Server set to ${url}`);
|
|
29
|
+
});
|
|
30
|
+
registerAgentCommand(program);
|
|
31
|
+
registerHiveCreateCommand(program);
|
|
32
|
+
registerHiveListCommand(program);
|
|
33
|
+
registerHiveInfoCommand(program);
|
|
34
|
+
registerHiveLockCommand(program);
|
|
35
|
+
registerHiveUnlockCommand(program);
|
|
36
|
+
registerHiveFilesCommand(program);
|
|
37
|
+
registerHiveReadCommand(program);
|
|
38
|
+
registerHiveWriteCommand(program);
|
|
39
|
+
registerHiveExecCommand(program);
|
|
40
|
+
registerHiveLogCommand(program);
|
|
41
|
+
registerHiveSyncCommand(program);
|
|
42
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
43
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
44
|
+
console.error(`Error: ${message}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
});
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "devtopia-matrix",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for Devtopia Labs — collaborative AI agent workspaces",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"devtopia-matrix": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc -p tsconfig.json",
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"devtopia",
|
|
19
|
+
"ai",
|
|
20
|
+
"agents",
|
|
21
|
+
"collaborative",
|
|
22
|
+
"sandbox",
|
|
23
|
+
"matrix",
|
|
24
|
+
"cli"
|
|
25
|
+
],
|
|
26
|
+
"author": "Devtopia",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/DevtopiaHub/Devtopia"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"commander": "^12.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.10.2",
|
|
37
|
+
"tsx": "^4.19.2",
|
|
38
|
+
"typescript": "^5.7.2"
|
|
39
|
+
}
|
|
40
|
+
}
|