@tagma/sdk 0.3.8 → 0.4.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/README.md +16 -2
- package/package.json +4 -2
- package/scripts/preinstall.js +38 -0
- package/src/schema.ts +6 -4
- package/src/validate-raw.ts +17 -6
package/README.md
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
# @tagma/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> ## ⚠️ Bun-only package — do **not** `npm install`
|
|
4
|
+
>
|
|
5
|
+
> ```bash
|
|
6
|
+
> bun add @tagma/sdk
|
|
7
|
+
> ```
|
|
8
|
+
>
|
|
9
|
+
> This package ships TypeScript source (`main: ./src/sdk.ts`) and relies on
|
|
10
|
+
> Bun's native `.ts` loader. `npm` / `yarn` / `pnpm` installs are blocked by a
|
|
11
|
+
> `preinstall` guard and will fail by design. Get Bun at <https://bun.sh>.
|
|
12
|
+
>
|
|
13
|
+
> *(The `npm i @tagma/sdk` line in the npmjs.com sidebar is auto-generated by
|
|
14
|
+
> the npm registry website and cannot be removed — please ignore it and use
|
|
15
|
+
> the command above.)*
|
|
4
16
|
|
|
5
|
-
|
|
17
|
+
A local AI task orchestration SDK for [Bun](https://bun.sh). Define multi-track pipelines in YAML, run AI coding agents (Claude Code, Codex, OpenCode) and shell commands in parallel with dependency resolution, approval gates, and lifecycle hooks.
|
|
6
18
|
|
|
7
19
|
## Install
|
|
8
20
|
|
|
21
|
+
**Requires Bun ≥ 1.3.** Node/npm are not supported.
|
|
22
|
+
|
|
9
23
|
```bash
|
|
10
24
|
bun add @tagma/sdk
|
|
11
25
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tagma/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,13 +15,15 @@
|
|
|
15
15
|
".": "./src/sdk.ts"
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
|
-
"src"
|
|
18
|
+
"src",
|
|
19
|
+
"scripts/preinstall.js"
|
|
19
20
|
],
|
|
20
21
|
"engines": {
|
|
21
22
|
"bun": ">=1.3"
|
|
22
23
|
},
|
|
23
24
|
"packageManager": "bun@1.3.7",
|
|
24
25
|
"scripts": {
|
|
26
|
+
"preinstall": "node scripts/preinstall.js",
|
|
25
27
|
"test": "bun test",
|
|
26
28
|
"release": "bun scripts/release.ts",
|
|
27
29
|
"release:publish": "bun scripts/release.ts --publish"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Preinstall guard — refuses installation under npm / yarn / pnpm.
|
|
2
|
+
// @tagma/sdk ships TypeScript source (main: ./src/sdk.ts) and relies on
|
|
3
|
+
// Bun's native .ts loader, so installing under Node-based managers leaves
|
|
4
|
+
// users with a broken package.
|
|
5
|
+
//
|
|
6
|
+
// Bun detection: we can't rely on process.versions.bun alone, because bun
|
|
7
|
+
// invokes lifecycle scripts via the interpreter named in the script command
|
|
8
|
+
// ("node scripts/preinstall.js" runs under node even when called from bun
|
|
9
|
+
// install). Bun does, however, set npm_config_user_agent to a string that
|
|
10
|
+
// starts with "bun/<version> ...", which is the canonical cross-manager
|
|
11
|
+
// signal. Check both for safety.
|
|
12
|
+
|
|
13
|
+
const ua = process.env.npm_config_user_agent || '';
|
|
14
|
+
if (process.versions.bun || ua.startsWith('bun/') || ua.startsWith('bun ')) {
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
19
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
20
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
21
|
+
|
|
22
|
+
process.stderr.write(
|
|
23
|
+
[
|
|
24
|
+
'',
|
|
25
|
+
red(bold(' @tagma/sdk requires Bun (>= 1.3).')),
|
|
26
|
+
'',
|
|
27
|
+
' This package ships TypeScript source and uses Bun\'s native .ts loader.',
|
|
28
|
+
' npm / yarn / pnpm cannot consume it.',
|
|
29
|
+
'',
|
|
30
|
+
' Install with:',
|
|
31
|
+
cyan(' bun add @tagma/sdk'),
|
|
32
|
+
'',
|
|
33
|
+
' Get Bun: https://bun.sh',
|
|
34
|
+
'',
|
|
35
|
+
].join('\n') + '\n',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
process.exit(1);
|
package/src/schema.ts
CHANGED
|
@@ -41,14 +41,16 @@ function validateRawTask(task: RawTaskConfig, trackId: string): void {
|
|
|
41
41
|
if (!task.id) throw new Error(`track "${trackId}": task.id is required`);
|
|
42
42
|
if (task.use) return; // template usage, validated later
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
if (!
|
|
44
|
+
const hasPromptKey = typeof task.prompt === 'string';
|
|
45
|
+
const hasCommandKey = typeof task.command === 'string';
|
|
46
|
+
if (!hasPromptKey && !hasCommandKey) {
|
|
47
47
|
throw new Error(`task "${task.id}": must have either "prompt" or "command"`);
|
|
48
48
|
}
|
|
49
|
-
if (
|
|
49
|
+
if (hasPromptKey && hasCommandKey) {
|
|
50
50
|
throw new Error(`task "${task.id}": cannot have both "prompt" and "command"`);
|
|
51
51
|
}
|
|
52
|
+
// Empty-content tasks (e.g. `prompt: ''`) are allowed at parse time and
|
|
53
|
+
// flagged as non-fatal validation errors by validate-raw.ts.
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
// ═══ Template Expansion ═══
|
package/src/validate-raw.ts
CHANGED
|
@@ -111,19 +111,30 @@ export function validateRaw(config: RawPipelineConfig): ValidationError[] {
|
|
|
111
111
|
// Template-based tasks: skip prompt/command checks (params validated at runtime)
|
|
112
112
|
if (task.use) continue;
|
|
113
113
|
|
|
114
|
-
const
|
|
115
|
-
const
|
|
114
|
+
const hasPromptKey = typeof task.prompt === 'string';
|
|
115
|
+
const hasCommandKey = typeof task.command === 'string';
|
|
116
|
+
const promptEmpty = hasPromptKey && task.prompt!.trim().length === 0;
|
|
117
|
+
const commandEmpty = hasCommandKey && task.command!.trim().length === 0;
|
|
116
118
|
|
|
117
|
-
if (
|
|
119
|
+
if (hasPromptKey && hasCommandKey) {
|
|
120
|
+
errors.push({
|
|
121
|
+
path: taskPath,
|
|
122
|
+
message: `Task "${task.id}": cannot have both "prompt" and "command"`,
|
|
123
|
+
});
|
|
124
|
+
} else if (!hasPromptKey && !hasCommandKey) {
|
|
118
125
|
errors.push({
|
|
119
126
|
path: taskPath,
|
|
120
127
|
message: `Task "${task.id}": must have "prompt" or "command"`,
|
|
121
128
|
});
|
|
122
|
-
}
|
|
123
|
-
if (hasPrompt && hasCommand) {
|
|
129
|
+
} else if (promptEmpty) {
|
|
124
130
|
errors.push({
|
|
125
131
|
path: taskPath,
|
|
126
|
-
message: `Task "${task.id}":
|
|
132
|
+
message: `Task "${task.id}": prompt content cannot be empty`,
|
|
133
|
+
});
|
|
134
|
+
} else if (commandEmpty) {
|
|
135
|
+
errors.push({
|
|
136
|
+
path: taskPath,
|
|
137
|
+
message: `Task "${task.id}": command content cannot be empty`,
|
|
127
138
|
});
|
|
128
139
|
}
|
|
129
140
|
|