@colony2/c2j 0.0.50 → 0.0.51

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 CHANGED
@@ -14,6 +14,14 @@ Use it when you want to:
14
14
 
15
15
  Examples below assume you are running from the repo root.
16
16
 
17
+ ## JobDB upgrade compatibility
18
+
19
+ The typed-route JobDB update requires fresh format-3 databases/artifact storage
20
+ and matching server/worker versions; existing jobs are not migrated. Job and
21
+ task types now travel separately, preserving existing task names. Read
22
+ [the upgrade notes](JOBDB_UPGRADE_NOTES.md) before deploying it. No existing data
23
+ is automatically reset.
24
+
17
25
  ## Command Summary
18
26
 
19
27
  ```bash
@@ -142,15 +150,34 @@ This is mainly useful when you want to target another cell by short name and nee
142
150
 
143
151
  ### `c2j init`
144
152
 
145
- Writes a commented `.c2j/config.yaml` template.
153
+ Writes a commented `.c2j/config.yaml` template and installs bundled c2j skills for Codex-compatible agents.
146
154
 
147
155
  ```bash
148
156
  c2j init
149
157
  c2j init --stdout
150
158
  c2j init --force
159
+ c2j init --no-skills
160
+ c2j init --with-op-skills
161
+ ```
162
+
163
+ The generated template can derive values from the Go module in the current repo when `base: go` is appropriate. `--stdout` prints only the config and does not install skills.
164
+
165
+ Bundled skill install controls:
166
+
167
+ ```bash
168
+ c2j init --skills-scope auto
169
+ c2j init --skills-scope project
170
+ c2j init --skills-scope user
171
+ c2j init --skills-force
151
172
  ```
152
173
 
153
- The generated template can derive values from the Go module in the current repo when `base: go` is appropriate.
174
+ `auto` currently installs to the project-local `.agents/skills` directory. `user` installs to `${CODEX_HOME:-$HOME/.codex}/skills`. Install metadata is recorded in `.c2j/skills-lock.yaml`.
175
+
176
+ You can also install the repo skills directly with the public `skills` tool:
177
+
178
+ ```bash
179
+ npx skills add colony-2/c2j
180
+ ```
154
181
 
155
182
  ## Submitting Jobs
156
183
 
@@ -489,6 +516,20 @@ Useful filters:
489
516
  - `--page-token`
490
517
  - `--all`
491
518
 
519
+ Filter by a pending task using a JSON route:
520
+
521
+ ```bash
522
+ c2j list --self --waiting-for '{"jobType":"recipe","taskType":"input:collect_user_input"}' --embed
523
+ ```
524
+
525
+ Repeat `--waiting-for` for multiple routes and `--job-type` for multiple job
526
+ types. These flags accept literal identifiers, not comma-separated lists;
527
+ `--waiting-for JOBTYPE:TASKTYPE` is no longer supported because either type may
528
+ contain colons. Identifiers are case-sensitive and are not trimmed or encoded.
529
+ List JSON exposes `next_route` (`jobType`, optional `taskType`) and `task_wait`
530
+ instead of the old `next_need` and flat `task_wait_*` fields. The human-readable
531
+ `NEXT` column also shows the route as JSON.
532
+
492
533
  ## Embedded Runtime
493
534
 
494
535
  `--embed` is shorthand for:
package/bin/cli.js ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+
9
+ const packageRoot = path.resolve(__dirname, "..");
10
+ const packageJson = require(path.join(packageRoot, "package.json"));
11
+ const packageName = packageJson.name.split("/").pop();
12
+ const binaryPath = path.join(packageRoot, "vendor", packageName);
13
+
14
+ if (!fs.existsSync(binaryPath)) {
15
+ console.error(
16
+ `${packageName} binary is missing. Reinstall the package or run npm rebuild ${packageJson.name}.`
17
+ );
18
+ process.exit(1);
19
+ }
20
+
21
+ const result = spawnSync(binaryPath, process.argv.slice(2), {
22
+ stdio: "inherit",
23
+ });
24
+
25
+ if (result.error) {
26
+ console.error(result.error.message);
27
+ process.exit(1);
28
+ }
29
+
30
+ process.exit(result.status === null ? 1 : result.status);
package/package.json CHANGED
@@ -1,85 +1,19 @@
1
1
  {
2
2
  "name": "@colony2/c2j",
3
- "type": "module",
4
- "version": "0.0.50",
3
+ "version": "0.0.51",
5
4
  "description": "c2j job system",
6
- "scripts": {
7
- "postinstall": "node install.js",
8
- "run": "node run-c2j.js"
9
- },
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://github.com/colony-2/c2j",
10
7
  "repository": {
11
8
  "type": "git",
12
9
  "url": "git+https://github.com/colony-2/c2j.git"
13
10
  },
14
- "author": {
15
- "email": "noreply@colony2.com",
16
- "name": "Colony 2"
17
- },
18
- "license": "Apache-2.0",
19
11
  "bugs": {
20
12
  "url": "https://github.com/colony-2/c2j/issues"
21
13
  },
22
- "homepage": "https://github.com/colony-2/c2j",
23
- "bin": {
24
- "c2j": "run-c2j.js"
25
- },
26
- "dependencies": {
27
- "jszip": "3.10.1",
28
- "proxy-agent": "8.0.1",
29
- "tar": "7.5.15"
30
- },
31
- "archives": {
32
- "darwin-arm64": {
33
- "name": "c2j_0.0.50_Darwin_arm64.tar.gz",
34
- "url": "https://github.com/colony-2/c2j/releases/download/v0.0.50/c2j_0.0.50_Darwin_arm64.tar.gz",
35
- "bins": [
36
- "c2j"
37
- ],
38
- "format": "tar.gz",
39
- "checksum": {
40
- "algorithm": "sha256",
41
- "digest": "b91fdeb7d21a36f2600eaf3ffc1661ce014ab442aecceae26fd4254d402656a1"
42
- }
43
- },
44
- "darwin-x64": {
45
- "name": "c2j_0.0.50_Darwin_x86_64.tar.gz",
46
- "url": "https://github.com/colony-2/c2j/releases/download/v0.0.50/c2j_0.0.50_Darwin_x86_64.tar.gz",
47
- "bins": [
48
- "c2j"
49
- ],
50
- "format": "tar.gz",
51
- "checksum": {
52
- "algorithm": "sha256",
53
- "digest": "ae12ffdda3d24b6b14f55dbd329b488f69c02907bde62589ca0c234deb7080ce"
54
- }
55
- },
56
- "linux-arm64": {
57
- "name": "c2j_0.0.50_Linux_arm64.tar.gz",
58
- "url": "https://github.com/colony-2/c2j/releases/download/v0.0.50/c2j_0.0.50_Linux_arm64.tar.gz",
59
- "bins": [
60
- "c2j"
61
- ],
62
- "format": "tar.gz",
63
- "checksum": {
64
- "algorithm": "sha256",
65
- "digest": "e198f9037bd138270aa92ac8b5caa4509b9f76056273d4191909e810d406ba72"
66
- }
67
- },
68
- "linux-x64": {
69
- "name": "c2j_0.0.50_Linux_x86_64.tar.gz",
70
- "url": "https://github.com/colony-2/c2j/releases/download/v0.0.50/c2j_0.0.50_Linux_x86_64.tar.gz",
71
- "bins": [
72
- "c2j"
73
- ],
74
- "format": "tar.gz",
75
- "checksum": {
76
- "algorithm": "sha256",
77
- "digest": "c8b720b153a9b7354887bfb9dd3f7d26ff615cd29a74dd8385fecda8f79d6e0f"
78
- }
79
- }
80
- },
81
- "engines": {
82
- "node": "\u003e=14.0.0"
14
+ "author": {
15
+ "name": "Colony 2",
16
+ "email": "noreply@colony2.com"
83
17
  },
84
18
  "keywords": [
85
19
  "sandbox",
@@ -88,5 +22,29 @@
88
22
  "agent",
89
23
  "cli",
90
24
  "security"
25
+ ],
26
+ "bin": {
27
+ "c2j": "bin/cli.js"
28
+ },
29
+ "scripts": {
30
+ "postinstall": "node scripts/postinstall.js"
31
+ },
32
+ "os": [
33
+ "darwin",
34
+ "linux"
35
+ ],
36
+ "cpu": [
37
+ "x64",
38
+ "arm64"
39
+ ],
40
+ "engines": {
41
+ "node": ">=14.0.0"
42
+ },
43
+ "files": [
44
+ "bin/",
45
+ "scripts/",
46
+ "vendor/",
47
+ "README.md",
48
+ "LICENSE"
91
49
  ]
92
- }
50
+ }
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const crypto = require("crypto");
6
+ const fs = require("fs");
7
+ const os = require("os");
8
+ const path = require("path");
9
+ const { execFileSync } = require("child_process");
10
+ const { get } = require("https");
11
+
12
+ const packageRoot = path.resolve(__dirname, "..");
13
+ const packageJson = require(path.join(packageRoot, "package.json"));
14
+ const packageName = packageJson.name.split("/").pop();
15
+ const version = packageJson.version;
16
+
17
+ const platforms = {
18
+ "linux:x64": ["Linux", "x86_64"],
19
+ "linux:arm64": ["Linux", "arm64"],
20
+ "darwin:x64": ["Darwin", "x86_64"],
21
+ "darwin:arm64": ["Darwin", "arm64"],
22
+ };
23
+
24
+ const platformKey = `${process.platform}:${process.arch}`;
25
+ const target = platforms[platformKey];
26
+
27
+ if (!target) {
28
+ throw new Error(`Unsupported platform for ${packageJson.name}: ${platformKey}`);
29
+ }
30
+
31
+ const repo = repositoryPath(packageJson);
32
+ const tag = `v${version}`;
33
+ const assetName = `${packageName}_${version}_${target[0]}_${target[1]}.tar.gz`;
34
+ const baseUrl = `https://github.com/${repo}/releases/download/${tag}`;
35
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${packageName}-`));
36
+ const archivePath = path.join(tempDir, assetName);
37
+ const checksumsPath = path.join(tempDir, "checksums.txt");
38
+ const extractDir = path.join(tempDir, "extract");
39
+ const vendorDir = path.join(packageRoot, "vendor");
40
+ const installedBinary = path.join(vendorDir, packageName);
41
+
42
+ fs.mkdirSync(extractDir);
43
+ fs.mkdirSync(vendorDir, { recursive: true });
44
+
45
+ downloadWithRetry(`${baseUrl}/${assetName}`, archivePath)
46
+ .then(() => downloadWithRetry(`${baseUrl}/checksums.txt`, checksumsPath))
47
+ .then(() => {
48
+ verifyChecksum(archivePath, checksumsPath, assetName);
49
+ execFileSync("tar", ["-xzf", archivePath, "-C", extractDir], {
50
+ stdio: "inherit",
51
+ });
52
+
53
+ const extractedBinary = path.join(extractDir, packageName);
54
+ if (!fs.existsSync(extractedBinary)) {
55
+ throw new Error(`Release archive did not contain ${packageName}`);
56
+ }
57
+
58
+ fs.copyFileSync(extractedBinary, installedBinary);
59
+ fs.chmodSync(installedBinary, 0o755);
60
+ })
61
+ .catch((error) => {
62
+ console.error(error.message || error);
63
+ process.exitCode = 1;
64
+ })
65
+ .finally(() => {
66
+ fs.rmSync(tempDir, { recursive: true, force: true });
67
+ });
68
+
69
+ function repositoryPath(pkg) {
70
+ const raw =
71
+ typeof pkg.repository === "string" ? pkg.repository : pkg.repository && pkg.repository.url;
72
+
73
+ if (!raw) {
74
+ throw new Error("package.json repository is required to locate release assets");
75
+ }
76
+
77
+ const match = raw.match(/github\.com[:/]([^/]+\/[^/.]+)(?:\.git)?/);
78
+ if (!match) {
79
+ throw new Error(`Unsupported repository URL for release assets: ${raw}`);
80
+ }
81
+
82
+ return match[1];
83
+ }
84
+
85
+ async function downloadWithRetry(url, destination) {
86
+ let lastError;
87
+
88
+ for (let attempt = 1; attempt <= 5; attempt += 1) {
89
+ try {
90
+ await download(url, destination);
91
+ return;
92
+ } catch (error) {
93
+ lastError = error;
94
+ if (attempt === 5) {
95
+ break;
96
+ }
97
+ await delay(attempt * 1000);
98
+ }
99
+ }
100
+
101
+ throw lastError;
102
+ }
103
+
104
+ function download(url, destination) {
105
+ return new Promise((resolve, reject) => {
106
+ const request = get(url, (response) => {
107
+ if (
108
+ response.statusCode >= 300 &&
109
+ response.statusCode < 400 &&
110
+ response.headers.location
111
+ ) {
112
+ response.resume();
113
+ download(response.headers.location, destination).then(resolve, reject);
114
+ return;
115
+ }
116
+
117
+ if (response.statusCode !== 200) {
118
+ response.resume();
119
+ reject(new Error(`Failed to download ${url}: HTTP ${response.statusCode}`));
120
+ return;
121
+ }
122
+
123
+ const file = fs.createWriteStream(destination, { mode: 0o600 });
124
+ response.pipe(file);
125
+ file.on("finish", () => file.close(resolve));
126
+ file.on("error", reject);
127
+ });
128
+
129
+ request.on("error", reject);
130
+ });
131
+ }
132
+
133
+ function delay(ms) {
134
+ return new Promise((resolve) => setTimeout(resolve, ms));
135
+ }
136
+
137
+ function verifyChecksum(archive, checksums, assetName) {
138
+ const expected = fs
139
+ .readFileSync(checksums, "utf8")
140
+ .split(/\r?\n/)
141
+ .map((line) => line.trim().split(/\s+/))
142
+ .find((parts) => parts[1] === assetName);
143
+
144
+ if (!expected) {
145
+ throw new Error(`No checksum found for ${assetName}`);
146
+ }
147
+
148
+ const actual = crypto.createHash("sha256").update(fs.readFileSync(archive)).digest("hex");
149
+ if (actual !== expected[0]) {
150
+ throw new Error(`Checksum mismatch for ${assetName}`);
151
+ }
152
+ }
package/install.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // This file was generated by GoReleaser. DO NOT EDIT.
4
-
5
- import { install } from "./lib.js";
6
- install();
package/lib.js DELETED
@@ -1,259 +0,0 @@
1
- // This file was generated by GoReleaser. DO NOT EDIT.
2
- import fs from "fs";
3
- import crypto from "crypto";
4
- import http from "http";
5
- import https from "https";
6
- import path from "path";
7
- import JSZip from "jszip";
8
- import { x as tarExtract } from "tar";
9
- import { ProxyAgent } from "proxy-agent";
10
- import { spawnSync } from "child_process";
11
- import { fileURLToPath } from "url";
12
-
13
- const { archives, name, version } = JSON.parse(
14
- fs.readFileSync(new URL("./package.json", import.meta.url), "utf8"),
15
- );
16
-
17
- const agent = new ProxyAgent();
18
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
-
20
- const getArchive = () => {
21
- let target = `${process.platform}-${process.arch}`;
22
- const archive = archives[target];
23
- if (!archive) {
24
- throw new Error(`No archive available for ${target}`);
25
- }
26
- return archive;
27
- };
28
-
29
- const binDir = path.join(__dirname, "bin");
30
-
31
- async function extractTar(tarPath, binaries, dir, wrappedIn) {
32
- try {
33
- const filesToExtract = wrappedIn
34
- ? binaries.map((bin) =>
35
- path.join(wrappedIn, bin).replace(/\\/g, "/"),
36
- )
37
- : binaries;
38
-
39
- await tarExtract({
40
- file: tarPath,
41
- cwd: dir,
42
- filter: (path) => filesToExtract.includes(path),
43
- });
44
-
45
- // If wrapped, move files from wrapped directory to bin directory
46
- if (wrappedIn) {
47
- const wrappedDir = path.join(dir, wrappedIn);
48
- for (const binary of binaries) {
49
- const srcPath = path.join(wrappedDir, binary);
50
- const destPath = path.join(dir, binary);
51
- if (fs.existsSync(srcPath)) {
52
- fs.renameSync(srcPath, destPath);
53
- }
54
- }
55
- // Clean up empty wrapped directory
56
- try {
57
- fs.rmSync(wrappedDir, { recursive: true, force: true });
58
- } catch (err) {
59
- // Ignore cleanup errors
60
- }
61
- }
62
-
63
- console.log(`Successfully extracted ${binaries} to "${dir}"`);
64
- } catch (err) {
65
- throw new Error(`Extraction failed: ${err.message}`);
66
- }
67
- }
68
-
69
- async function extractZip(zipPath, binaries, dir, wrappedIn) {
70
- try {
71
- const zipData = fs.readFileSync(zipPath);
72
- const zip = await JSZip.loadAsync(zipData);
73
-
74
- for (const binary of binaries) {
75
- const binaryPath = wrappedIn
76
- ? path.join(wrappedIn, binary).replace(/\\/g, "/")
77
- : binary;
78
-
79
- if (!zip.files[binaryPath]) {
80
- throw new Error(
81
- `Error: ${binaryPath} does not exist in ${zipPath}`,
82
- );
83
- }
84
-
85
- const content = await zip.files[binaryPath].async("nodebuffer");
86
- if (!fs.existsSync(dir)) {
87
- fs.mkdirSync(dir, { recursive: true });
88
- }
89
- const file = path.join(dir, binary);
90
- fs.writeFileSync(file, content);
91
- fs.chmodSync(file, "755");
92
- console.log(`Successfully extracted "${binary}" to "${dir}"`);
93
- }
94
- } catch (err) {
95
- throw new Error(`Extraction failed: ${err.message}`);
96
- }
97
- }
98
-
99
- const run = async (bin) => {
100
- await install();
101
- if (process.platform === "win32") {
102
- bin += ".exe";
103
- }
104
- const [, , ...args] = process.argv;
105
- let result = spawnSync(path.join(binDir, bin), args, {
106
- cwd: process.cwd(),
107
- stdio: "inherit",
108
- });
109
- if (result.error) {
110
- console.error(result.error);
111
- }
112
- return result.status;
113
- };
114
-
115
- const install = async () => {
116
- try {
117
- let archive = getArchive();
118
- if (await exists(archive)) {
119
- return;
120
- }
121
- let tmp = fs.mkdtempSync("archive-");
122
- let archivePath = path.join(tmp, archive.name);
123
- await download(archive.url, archivePath);
124
- verify(archivePath, archive.checksum);
125
-
126
- if (!fs.existsSync(binDir)) {
127
- fs.mkdirSync(binDir);
128
- }
129
- switch (archive.format) {
130
- case "binary":
131
- const bin = path.join(binDir, archive.bins[0]);
132
- fs.copyFileSync(archivePath, bin);
133
- fs.chmodSync(bin, 0o755);
134
- break;
135
- case "zip":
136
- await extractZip(
137
- archivePath,
138
- archive.bins,
139
- binDir,
140
- archive.wrappedIn,
141
- );
142
- break;
143
- case "tar":
144
- case "tar.gz":
145
- case "tgz":
146
- await extractTar(
147
- archivePath,
148
- archive.bins,
149
- binDir,
150
- archive.wrappedIn,
151
- );
152
- break;
153
- case "tar.zst":
154
- case "tzst":
155
- case "tar.xz":
156
- case "txz":
157
- default:
158
- throw new Error(`unsupported format: ${archive.format}`);
159
- }
160
- console.log(`Installed ${name} ${version} to ${binDir}`);
161
- } catch (err) {
162
- throw new Error(`Installation failed: ${err.message}`);
163
- }
164
- };
165
-
166
- const verify = (filename, checksum) => {
167
- if (checksum.algorithm == "" || checksum.digest == "") {
168
- console.warn("Warning: No checksum provided for verification");
169
- return;
170
- }
171
- let digest = crypto
172
- .createHash(checksum.algorithm)
173
- .update(fs.readFileSync(filename))
174
- .digest("hex");
175
- if (digest != checksum.digest) {
176
- throw new Error(
177
- `${filename}: ${checksum.algorithm} does not match, expected ${checksum.digest}, got ${digest}`,
178
- );
179
- }
180
- };
181
-
182
- const download = async (url, filename, maxRedirects = 10) => {
183
- try {
184
- console.log(`Downloading ${url} to ${filename}...`);
185
- const dir = path.dirname(filename);
186
- if (!fs.existsSync(dir)) {
187
- fs.mkdirSync(dir, { recursive: true });
188
- }
189
-
190
- return new Promise((resolve, reject) => {
191
- const parsedUrl = new URL(url);
192
- const mod = parsedUrl.protocol === "https:" ? https : http;
193
-
194
- const request = mod.get(url, { agent }, (response) => {
195
- if (
196
- response.statusCode >= 300 &&
197
- response.statusCode < 400 &&
198
- response.headers.location
199
- ) {
200
- if (maxRedirects <= 0) {
201
- reject(new Error("Too many redirects"));
202
- return;
203
- }
204
- download(response.headers.location, filename, maxRedirects - 1)
205
- .then(resolve)
206
- .catch(reject);
207
- return;
208
- }
209
-
210
- if (response.statusCode !== 200) {
211
- reject(
212
- new Error(
213
- `HTTP ${response.statusCode}: ${response.statusMessage}`,
214
- ),
215
- );
216
- return;
217
- }
218
-
219
- const writer = fs.createWriteStream(filename);
220
- response.pipe(writer);
221
-
222
- writer.on("finish", () => {
223
- console.log(`Download complete: ${filename}`);
224
- resolve(dir);
225
- });
226
-
227
- writer.on("error", (err) => {
228
- console.error(`Error writing file: ${err.message}`);
229
- reject(err);
230
- });
231
- });
232
-
233
- request.on("error", (err) => {
234
- reject(new Error(`Request failed: ${err.message}`));
235
- });
236
-
237
- request.setTimeout(300000, () => {
238
- request.destroy();
239
- reject(new Error("Request timed out"));
240
- });
241
- });
242
- } catch (err) {
243
- throw new Error(`Download failed: ${err.message}`);
244
- }
245
- };
246
-
247
- function exists(archive) {
248
- if (!fs.existsSync(binDir)) {
249
- return false;
250
- }
251
- return archive.bins.every((bin) => fs.existsSync(path.join(binDir, bin)));
252
- }
253
-
254
- export {
255
- install,
256
- run,
257
- getArchive,
258
- download,
259
- };
package/run-c2j.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // This file was generated by GoReleaser. DO NOT EDIT.
4
-
5
- import { run } from "./lib.js";
6
- run("c2j").then((code) => process.exit(code));