#!/usr/bin/env node
/**
* Mini CLI example for hyper-orgs
*
* Usage:
* node examples/cli.js init Create an orgs store
* node examples/cli.js info Show key + counts + writers
* node examples/cli.js create <name> [group|user] Create an org
* node examples/cli.js list [group|user|all] List orgs
* node examples/cli.js show <name> Show org details
* node examples/cli.js add-member <org> <userId> [role] Add a member
* node examples/cli.js rm-member <org> <userId> Remove a member
* node examples/cli.js add-resource <org> <resource> Attach a resource
* node examples/cli.js rm-resource <org> <resource> Detach a resource
* node examples/cli.js grant <subject> <org> [perm] Grant a user access to an org
* node examples/cli.js invite <org> [perm] Create an invite
* node examples/cli.js request <org> <userId> [perm] Request access
* node examples/cli.js check <userId> <resource> Check access
* node examples/cli.js delete <name> Delete empty org
* node examples/cli.js demo Run a full demo
*
* Storage: HYPER_ORGS_STORE env var (default: ./.hyper-orgs-cli)
*
* NOTE: this CLI uses its own storage path. The web example uses a different
* default. They don't share state — RocksDB single-writer lock.
*/
import Corestore from 'corestore'
import { HyperOrgs } from '../index.js'
const storagePath = process.env.HYPER_ORGS_STORE || './.hyper-orgs-cli'
const actor = process.env.USER || 'me'
const args = process.argv.slice(2)
const cmd = args[0]
async function open () {
const store = new Corestore(storagePath)
await store.ready()
const orgs = new HyperOrgs(store, { namespace: 'orgs' })
await orgs.ready()
return { orgs, store }
}
async function close ({ orgs, store }) {
await orgs.close()
await store.close()
}
function printOrg (org) {
console.log('Org: %s (%s)', org.name, org.type)
console.log(' displayName: %s', org.displayName)
if (org.description) console.log(' description: %s', org.description)
console.log(' visibility: %s', org.visibility)
if (org.type === 'user' && org.userId) console.log(' userId: %s', org.userId)
if (org.members.length > 0) {
console.log(' members:')
for (const m of org.members) console.log(' - %s (%s)', m.userId, m.role)
}
if (org.resources.length > 0) {
console.log(' resources: %s', org.resources.join(', '))
}
}
async function main () {
if (cmd === 'init') {
const ctx = await open()
console.log('HyperOrgs initialized at: %s', storagePath)
console.log(' key: %s', ctx.orgs.keyHex)
console.log(' localKey: %s', ctx.orgs.localKeyHex)
console.log(' writable: %s', ctx.orgs.writable)
await close(ctx)
return
}
if (cmd === 'info') {
const ctx = await open()
const all = await ctx.orgs.list()
const writers = await ctx.orgs.listWriters()
const grants = await ctx.orgs.listGrants()
const invites = await ctx.orgs.listInvites()
const requests = await ctx.orgs.listAccessRequests()
console.log('HyperOrgs: %s', storagePath)
console.log(' key: %s', ctx.orgs.keyHex)
console.log(' localKey: %s', ctx.orgs.localKeyHex)
console.log(' writable: %s', ctx.orgs.writable)
console.log(' orgs: %d', all.length)
console.log(' writers: %d', writers.length)
console.log(' grants: %d', grants.length)
console.log(' invites: %d', invites.length)
console.log(' requests: %d', requests.length)
await close(ctx)
return
}
if (cmd === 'create') {
const name = args[1]
const type = args[2] || 'group'
if (!name) { console.error('Usage: cli.js create <name> [group|user]'); process.exit(1) }
const ctx = await open()
const org = await ctx.orgs.create(name, type, {
userId: type === 'user' ? actor : null
})
console.log('Created %s "%s"', type, name)
printOrg(org)
await close(ctx)
return
}
if (cmd === 'list') {
const filter = args[1]
const opts = {}
if (filter === 'group' || filter === 'user') opts.type = filter
const ctx = await open()
const all = await ctx.orgs.list(opts)
if (all.length === 0) {
console.log('(no orgs)')
} else {
for (const o of all) {
const icon = o.type === 'user' ? 'user' : 'group'
const name = (o.name + ' '.repeat(20)).slice(0, 20)
console.log('[%s] %s %s', icon, name, o.displayName)
}
console.log('\n%d orgs', all.length)
}
await close(ctx)
return
}
if (cmd === 'show') {
const name = args[1]
if (!name) { console.error('Usage: cli.js show <name>'); process.exit(1) }
const ctx = await open()
const org = await ctx.orgs.get(name)
if (!org) {
console.error('Org "%s" not found', name)
await close(ctx)
process.exit(1)
}
printOrg(org)
await close(ctx)
return
}
if (cmd === 'add-member') {
const [_, org, userId, role = 'member'] = args
if (!org || !userId) { console.error('Usage: cli.js add-member <org> <userId> [role]'); process.exit(1) }
const ctx = await open()
const updated = await ctx.orgs.addMember(org, userId, role)
console.log('Added %s as %s to %s', userId, role, org)
printOrg(updated)
await close(ctx)
return
}
if (cmd === 'rm-member') {
const [_, org, userId] = args
if (!org || !userId) { console.error('Usage: cli.js rm-member <org> <userId>'); process.exit(1) }
const ctx = await open()
await ctx.orgs.removeMember(org, userId)
console.log('Removed %s from %s', userId, org)
await close(ctx)
return
}
if (cmd === 'add-resource') {
const [_, org, resource] = args
if (!org || !resource) { console.error('Usage: cli.js add-resource <org> <resource>'); process.exit(1) }
const ctx = await open()
const updated = await ctx.orgs.addResource(org, resource)
console.log('Added resource %s to %s', resource, org)
printOrg(updated)
await close(ctx)
return
}
if (cmd === 'rm-resource') {
const [_, org, resource] = args
if (!org || !resource) { console.error('Usage: cli.js rm-resource <org> <resource>'); process.exit(1) }
const ctx = await open()
await ctx.orgs.removeResource(org, resource)
console.log('Removed resource %s from %s', resource, org)
await close(ctx)
return
}
if (cmd === 'grant') {
const [_, subject, org, permission = 'write'] = args
if (!subject || !org) { console.error('Usage: cli.js grant <subject> <org> [perm]'); process.exit(1) }
const ctx = await open()
const grant = await ctx.orgs.createGrant({
subjectType: 'user',
subjectId: subject,
grantType: 'group',
groupName: org,
permission,
grantedBy: actor
})
console.log('Grant %s: %s → %s (%s)', grant.grantId, subject, org, permission)
await close(ctx)
return
}
if (cmd === 'invite') {
const [_, org, permission = 'write'] = args
if (!org) { console.error('Usage: cli.js invite <org> [perm]'); process.exit(1) }
const ctx = await open()
const invite = await ctx.orgs.createInvite({
inviteType: 'group',
targetGroup: org,
permission,
maxUses: 1,
createdBy: actor
})
console.log('Invite %s: %s (%s, 1 use)', invite.inviteId, org, permission)
console.log('Share this inviteId with the target subject.')
await close(ctx)
return
}
if (cmd === 'request') {
const [_, org, userId, permission = 'write'] = args
if (!org || !userId) { console.error('Usage: cli.js request <org> <userId> [perm]'); process.exit(1) }
const ctx = await open()
const req = await ctx.orgs.createAccessRequest({
subjectType: 'user',
subjectId: userId,
requestType: 'group',
groupName: org,
permission
})
console.log('Request %s: %s → %s (%s) status=%s', req.requestId, userId, org, permission, req.status)
await close(ctx)
return
}
if (cmd === 'check') {
const [_, userId, resource] = args
if (!userId || !resource) { console.error('Usage: cli.js check <userId> <resource>'); process.exit(1) }
const ctx = await open()
const allowed = await ctx.orgs.checkAccess({
userId,
resourceName: resource,
requiredPermission: 'write'
})
console.log('%s can write %s? %s', userId, resource, allowed ? 'YES' : 'NO')
await close(ctx)
return
}
if (cmd === 'delete') {
const name = args[1]
if (!name) { console.error('Usage: cli.js delete <name>'); process.exit(1) }
const ctx = await open()
await ctx.orgs.delete(name)
console.log('Deleted %s', name)
await close(ctx)
return
}
if (cmd === 'demo') {
const ctx = await open()
console.log('--- hyper-orgs demo ---\n')
console.log('1. Create a user org and a group org')
await ctx.orgs.create('alice', 'user', { userId: 'alice-key-hex' })
await ctx.orgs.create('my-team', 'group', {
displayName: 'My Team',
description: 'A test team'
})
console.log('2. Add members to the group')
await ctx.orgs.addMember('my-team', 'alice', 'admin')
await ctx.orgs.addMember('my-team', 'bob', 'member')
console.log('3. Attach two resources to the team')
await ctx.orgs.addResource('my-team', 'project-a')
await ctx.orgs.addResource('my-team', 'project-b')
console.log('4. Grant alice "write" on the whole team')
await ctx.orgs.createGrant({
subjectType: 'user', subjectId: 'alice',
grantType: 'group', groupName: 'my-team',
permission: 'write',
grantedBy: 'demo'
})
console.log('5. Create an invite for anyone who wants to join')
const invite = await ctx.orgs.createInvite({
inviteType: 'group', targetGroup: 'my-team',
permission: 'write', maxUses: 3,
createdBy: 'demo'
})
console.log(' invite: %s', invite.inviteId)
console.log('6. Someone requests access')
const req = await ctx.orgs.createAccessRequest({
subjectType: 'user', subjectId: 'carol',
requestType: 'group', groupName: 'my-team',
permission: 'write', reason: 'want to help'
})
console.log('7. Admin approves the request')
await ctx.orgs.approveAccessRequest(req.requestId, 'demo')
console.log('\n8. Summary:')
const team = await ctx.orgs.get('my-team')
printOrg(team)
console.log()
const allowed = await ctx.orgs.checkAccess({
userId: 'alice',
resourceName: 'project-a',
requiredPermission: 'write'
})
console.log('alice can write project-a? %s', allowed ? 'YES' : 'NO')
const carolAllowed = await ctx.orgs.checkAccess({
userId: 'carol',
resourceName: 'project-a',
requiredPermission: 'write'
})
console.log('carol can write project-a? %s', carolAllowed ? 'YES' : 'NO')
await close(ctx)
return
}
console.log('hyper-orgs example CLI')
console.log(' init Create an orgs store')
console.log(' info Show key + counts + writers')
console.log(' create <name> [group|user] Create an org')
console.log(' list [group|user|all] List orgs')
console.log(' show <name> Show org details')
console.log(' add-member <org> <userId> [role] Add a member')
console.log(' rm-member <org> <userId> Remove a member')
console.log(' add-resource <org> <resource> Attach a resource')
console.log(' rm-resource <org> <resource> Detach a resource')
console.log(' grant <subject> <org> [perm] Grant access to an org')
console.log(' invite <org> [perm] Create an invite')
console.log(' request <org> <userId> [perm] Request access')
console.log(' check <userId> <resource> Check access')
console.log(' delete <name> Delete empty org')
console.log(' demo Run a full demo')
console.log('')
console.log('Storage: HYPER_ORGS_STORE env var (default: ./.hyper-orgs-cli)')
console.log('Note: this CLI does NOT share state with examples/web.js — they use')
console.log('different storage paths because RocksDB only allows one writer.')
}
main().catch((err) => { console.error(err.message); process.exit(1) })