Add CLI package
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
dist_*/
|
||||||
|
dist_ts/
|
||||||
|
coverage/
|
||||||
|
.nyc_output/
|
||||||
|
.nogit/
|
||||||
|
.playwright-mcp/
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "@smarthome.exchange/cli",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": false,
|
||||||
|
"description": "Command line interface for smarthome.exchange.",
|
||||||
|
"bin": {
|
||||||
|
"shx": "./cli.js"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": "./dist_ts/index.js"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"author": "Task Venture Capital GmbH",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"test": "tstest test/ --verbose --logfile --timeout 60",
|
||||||
|
"build": "tsbuild tsfolders --allowimplicitany",
|
||||||
|
"buildDocs": "tsdoc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@smarthome.exchange/api": "workspace:*",
|
||||||
|
"@smarthome.exchange/hub": "workspace:*",
|
||||||
|
"@smarthome.exchange/interfaces": "workspace:*",
|
||||||
|
"@smarthome.exchange/sdk": "workspace:*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@git.zone/tsbuild": "^4.4.0",
|
||||||
|
"@git.zone/tsdoc": "^2.0.3",
|
||||||
|
"@git.zone/tsrun": "^2.0.3",
|
||||||
|
"@git.zone/tstest": "^3.6.3",
|
||||||
|
"@types/node": "^25.6.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"ts/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"dist_*/**/*",
|
||||||
|
"dist_ts/**/*",
|
||||||
|
"cli.js",
|
||||||
|
"readme.md"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"last 1 chrome versions"
|
||||||
|
],
|
||||||
|
"packageManager": "pnpm@10.28.2"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# @smarthome.exchange/cli
|
||||||
|
|
||||||
|
The `shx` command line interface for initializing homes, agents, and automations.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
import { normalizeFileStem } from '../ts/index.js';
|
||||||
|
|
||||||
|
tap.test('normalizes generated file names', async () => {
|
||||||
|
expect(normalizeFileStem('Plant Sitter!')).toEqual('plant-sitter');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import * as plugins from './plugins.js';
|
||||||
|
|
||||||
|
export const normalizeFileStem = (valueArg: string) => {
|
||||||
|
return valueArg
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9]+/g, '-')
|
||||||
|
.replace(/^-+|-+$/g, '') || 'new-item';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHelpText = () => `smarthome.exchange CLI
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
shx help
|
||||||
|
shx init [name]
|
||||||
|
shx open
|
||||||
|
shx agent new <name>
|
||||||
|
shx automation new <name>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const runCli = async (argvArg = process.argv.slice(2)) => {
|
||||||
|
const [command, subcommand, nameArg] = argvArg;
|
||||||
|
|
||||||
|
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
||||||
|
console.log(getHelpText());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'init') {
|
||||||
|
await initHome(subcommand || 'birch-lane');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'open') {
|
||||||
|
console.log('Open http://localhost:8080 after starting @smarthome.exchange/hub.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'agent' && subcommand === 'new') {
|
||||||
|
await createAgent(nameArg || 'custom-agent');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'automation' && subcommand === 'new') {
|
||||||
|
await createAutomation(nameArg || 'new-automation');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown command: ${argvArg.join(' ')}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initHome = async (nameArg: string) => {
|
||||||
|
const homeName = normalizeFileStem(nameArg);
|
||||||
|
await plugins.fs.mkdir('.shx/agents', { recursive: true });
|
||||||
|
await plugins.fs.mkdir('.shx/automations', { recursive: true });
|
||||||
|
await plugins.fs.mkdir('.shx/dashboards', { recursive: true });
|
||||||
|
await plugins.fs.writeFile(
|
||||||
|
'.shx/config.json',
|
||||||
|
`${JSON.stringify({ name: homeName, createdAt: new Date().toISOString() }, null, 2)}\n`
|
||||||
|
);
|
||||||
|
console.log(`Initialized .shx home: ${homeName}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createAgent = async (nameArg: string) => {
|
||||||
|
const fileStem = normalizeFileStem(nameArg);
|
||||||
|
const targetPath = plugins.path.join('.shx', 'agents', `${fileStem}.ts`);
|
||||||
|
await plugins.fs.mkdir(plugins.path.dirname(targetPath), { recursive: true });
|
||||||
|
await plugins.fs.writeFile(
|
||||||
|
targetPath,
|
||||||
|
`import type { data } from '@smarthome.exchange/interfaces';\n\nexport const agent: data.IAgentDefinition = {\n id: '${fileStem}',\n name: '${nameArg}',\n role: 'Custom household agent',\n glyph: 'custom',\n color: '#60a5fa',\n mode: 'ask',\n model: 'local:small',\n scopes: ['*.read'],\n systemPrompt: 'Describe the agent behavior here.',\n enabled: true,\n};\n`
|
||||||
|
);
|
||||||
|
console.log(`Created ${targetPath}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createAutomation = async (nameArg: string) => {
|
||||||
|
const fileStem = normalizeFileStem(nameArg);
|
||||||
|
const targetPath = plugins.path.join('.shx', 'automations', `${fileStem}.ts`);
|
||||||
|
await plugins.fs.mkdir(plugins.path.dirname(targetPath), { recursive: true });
|
||||||
|
await plugins.fs.writeFile(
|
||||||
|
targetPath,
|
||||||
|
`import { on, devices } from '@smarthome.exchange/sdk';\n\non({ id: 'manual:${fileStem}', kind: 'manual', expression: '${fileStem}' }, async () => {\n await devices.read('climate.office');\n});\n`
|
||||||
|
);
|
||||||
|
console.log(`Created ${targetPath}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.argv[1]?.endsWith('/cli.js') || process.argv[1]?.endsWith('\\cli.js')) {
|
||||||
|
runCli().catch((errorArg) => {
|
||||||
|
console.error(errorArg.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './cli.js';
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Native scope
|
||||||
|
import * as fs from 'node:fs/promises';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
|
||||||
|
export { fs, path };
|
||||||
|
|
||||||
|
// Project scope
|
||||||
|
import * as shxApi from '@smarthome.exchange/api';
|
||||||
|
import * as shxHub from '@smarthome.exchange/hub';
|
||||||
|
import * as shxInterfaces from '@smarthome.exchange/interfaces';
|
||||||
|
import * as shxSdk from '@smarthome.exchange/sdk';
|
||||||
|
|
||||||
|
export { shxApi, shxHub, shxInterfaces, shxSdk };
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"dist_*/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user