@locusai/sdk 0.1.1
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_GUIDE.md +269 -0
- package/package.json +44 -0
package/PACKAGE_GUIDE.md
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Locus Package Author Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to build, test, and publish a community package for
|
|
4
|
+
the [Locus](https://github.com/locusai/locus) CLI.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Naming convention
|
|
9
|
+
|
|
10
|
+
All Locus packages must be published to npm with the prefix `locus-`:
|
|
11
|
+
|
|
12
|
+
| Short name | npm package name |
|
|
13
|
+
|------------|-------------------|
|
|
14
|
+
| `telegram` | `locus-telegram` |
|
|
15
|
+
| `slack` | `locus-slack` |
|
|
16
|
+
| `jira` | `locus-jira` |
|
|
17
|
+
|
|
18
|
+
Scoped packages (e.g. `@myorg/locus-telegram`) are also accepted and will be
|
|
19
|
+
used as-is.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## `package.json` manifest format
|
|
24
|
+
|
|
25
|
+
Every Locus package **must** include a `"locus"` field in its `package.json`.
|
|
26
|
+
This field tells the Locus CLI how to display and invoke your package.
|
|
27
|
+
|
|
28
|
+
```jsonc
|
|
29
|
+
{
|
|
30
|
+
"name": "locus-telegram",
|
|
31
|
+
"version": "1.0.0",
|
|
32
|
+
"description": "Remote-control Locus via Telegram",
|
|
33
|
+
"bin": {
|
|
34
|
+
"locus-telegram": "./bin/locus-telegram.js"
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
// Required: Locus package manifest
|
|
38
|
+
"locus": {
|
|
39
|
+
"displayName": "Telegram",
|
|
40
|
+
"description": "Remote-control your Locus agent from Telegram",
|
|
41
|
+
"commands": ["telegram"],
|
|
42
|
+
"version": "1.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Field | Type | Description |
|
|
48
|
+
|---------------|------------|------------------------------------------------------------------|
|
|
49
|
+
| `displayName` | `string` | Human-readable name shown in `locus packages list`. |
|
|
50
|
+
| `description` | `string` | One-line description shown in `locus packages list`. |
|
|
51
|
+
| `commands` | `string[]` | Sub-commands contributed. Used in `locus pkg <name>` dispatch. |
|
|
52
|
+
| `version` | `string` | Semver version — should mirror the npm package version. |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Binary entry point
|
|
57
|
+
|
|
58
|
+
Your package must expose a binary. Locus discovers it via
|
|
59
|
+
`~/.locus/packages/node_modules/.bin/locus-<name>` after installation.
|
|
60
|
+
|
|
61
|
+
In `package.json`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
"bin": {
|
|
65
|
+
"locus-telegram": "./bin/locus-telegram.js"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The binary should be executable Node.js (shebang `#!/usr/bin/env node`).
|
|
70
|
+
|
|
71
|
+
When invoked via `locus pkg telegram [args...]`, the remaining args are
|
|
72
|
+
forwarded to your binary verbatim.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Using `@locusai/sdk`
|
|
77
|
+
|
|
78
|
+
Install the SDK as a dev dependency (it is a peer contract, not bundled):
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
npm install --save-dev @locusai/sdk
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Reading project config
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { readLocusConfig } from "@locusai/sdk";
|
|
88
|
+
|
|
89
|
+
// Reads ~/.locus/config.json + ./.locus/config.json and merges them.
|
|
90
|
+
const config = readLocusConfig();
|
|
91
|
+
|
|
92
|
+
console.log(config.github.owner); // "myorg"
|
|
93
|
+
console.log(config.ai.model); // "claude-sonnet-4-6"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Invoking `locus` sub-commands
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { invokeLocus, invokeLocusStream } from "@locusai/sdk";
|
|
100
|
+
|
|
101
|
+
// Captured output (blocking)
|
|
102
|
+
const result = await invokeLocus(["run", "42"]);
|
|
103
|
+
if (result.exitCode !== 0) {
|
|
104
|
+
console.error("locus run failed:", result.stderr);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Streaming (non-blocking)
|
|
108
|
+
const child = invokeLocusStream(["run", "42"]);
|
|
109
|
+
child.stdout?.on("data", (chunk: Buffer) => process.stdout.write(chunk));
|
|
110
|
+
child.on("exit", (code) => console.log("exited with", code));
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Structured logger
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { createLogger } from "@locusai/sdk";
|
|
117
|
+
|
|
118
|
+
const logger = createLogger("telegram");
|
|
119
|
+
|
|
120
|
+
logger.info("Bot started");
|
|
121
|
+
logger.warn("Rate limit approaching", { remaining: 5 });
|
|
122
|
+
logger.error("Connection failed", { code: 503 });
|
|
123
|
+
logger.debug("Raw update", { update_id: 12345 }); // only shown with LOCUS_DEBUG=1
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Output uses the same prefix symbols as the Locus CLI (`●`, `⚠`, `✗`, `⋯`).
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Minimal end-to-end example
|
|
131
|
+
|
|
132
|
+
Below is a complete minimal Locus package (`locus-hello`).
|
|
133
|
+
|
|
134
|
+
### Directory structure
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
locus-hello/
|
|
138
|
+
├── bin/
|
|
139
|
+
│ └── locus-hello.js
|
|
140
|
+
├── src/
|
|
141
|
+
│ └── index.ts
|
|
142
|
+
├── package.json
|
|
143
|
+
└── tsconfig.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `package.json`
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"name": "locus-hello",
|
|
151
|
+
"version": "1.0.0",
|
|
152
|
+
"description": "Example Locus package",
|
|
153
|
+
"type": "module",
|
|
154
|
+
"bin": {
|
|
155
|
+
"locus-hello": "./bin/locus-hello.js"
|
|
156
|
+
},
|
|
157
|
+
"locus": {
|
|
158
|
+
"displayName": "Hello",
|
|
159
|
+
"description": "A minimal example Locus package",
|
|
160
|
+
"commands": ["hello"],
|
|
161
|
+
"version": "1.0.0"
|
|
162
|
+
},
|
|
163
|
+
"scripts": {
|
|
164
|
+
"build": "tsc"
|
|
165
|
+
},
|
|
166
|
+
"devDependencies": {
|
|
167
|
+
"@locusai/sdk": "^0.1.0",
|
|
168
|
+
"typescript": "^5.0.0"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `src/index.ts`
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { createLogger, readLocusConfig } from "@locusai/sdk";
|
|
177
|
+
|
|
178
|
+
const logger = createLogger("hello");
|
|
179
|
+
|
|
180
|
+
export async function run(): Promise<void> {
|
|
181
|
+
const config = readLocusConfig();
|
|
182
|
+
logger.info(`Hello from locus-hello! Repo: ${config.github.owner}/${config.github.repo}`);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `bin/locus-hello.js`
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
#!/usr/bin/env node
|
|
190
|
+
import "../dist/index.js";
|
|
191
|
+
|
|
192
|
+
const { run } = await import("../dist/index.js");
|
|
193
|
+
await run();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Make it executable:
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
chmod +x bin/locus-hello.js
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Testing locally with `npm link`
|
|
205
|
+
|
|
206
|
+
1. Build your package:
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
npm run build
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
2. Link it globally:
|
|
213
|
+
|
|
214
|
+
```sh
|
|
215
|
+
npm link
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
3. Install it into Locus's packages directory:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
locus install locus-hello
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Or, for a faster dev loop, symlink directly:
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
mkdir -p ~/.locus/packages/node_modules
|
|
228
|
+
ln -s $(pwd) ~/.locus/packages/node_modules/locus-hello
|
|
229
|
+
mkdir -p ~/.locus/packages/node_modules/.bin
|
|
230
|
+
ln -s $(pwd)/bin/locus-hello.js ~/.locus/packages/node_modules/.bin/locus-hello
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
4. Run your package:
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
locus pkg hello
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Publishing to npm
|
|
242
|
+
|
|
243
|
+
1. Make sure the `"locus"` field is present in your `package.json`.
|
|
244
|
+
2. Ensure the binary listed in `"bin"` is executable.
|
|
245
|
+
3. Build your package.
|
|
246
|
+
4. Publish:
|
|
247
|
+
|
|
248
|
+
```sh
|
|
249
|
+
npm publish --access public
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
After publishing, users can install your package with:
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
locus install hello # short name (locus-hello)
|
|
256
|
+
locus install locus-hello
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Checklist before publishing
|
|
262
|
+
|
|
263
|
+
- [ ] Package name starts with `locus-` (or is scoped)
|
|
264
|
+
- [ ] `"locus"` field present in `package.json` with all required keys
|
|
265
|
+
- [ ] Binary listed in `"bin"` exists and has the shebang line
|
|
266
|
+
- [ ] Binary is executable (`chmod +x`)
|
|
267
|
+
- [ ] Package builds cleanly with no TypeScript errors
|
|
268
|
+
- [ ] Tested locally with `locus pkg <name>`
|
|
269
|
+
- [ ] `README.md` explains what the package does and how to configure it
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@locusai/sdk",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "SDK for building Locus-compatible community packages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"PACKAGE_GUIDE.md",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target node --format esm && bun build ./src/index.ts --outfile ./dist/index.cjs --target node --format cjs && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"lint": "biome lint .",
|
|
30
|
+
"format": "biome format --write .",
|
|
31
|
+
"clean": "rm -rf dist node_modules"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"locus",
|
|
35
|
+
"sdk",
|
|
36
|
+
"package",
|
|
37
|
+
"plugin",
|
|
38
|
+
"community"
|
|
39
|
+
],
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18"
|
|
43
|
+
}
|
|
44
|
+
}
|