@rubytech/taskmaster 1.0.89 → 1.0.91
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/dist/agents/apply-patch.js +3 -1
- package/dist/agents/bash-tools.exec.js +3 -1
- package/dist/agents/bash-tools.process.js +3 -1
- package/dist/build-info.json +2 -2
- package/dist/gateway/server-methods/update.js +8 -7
- package/dist/memory/internal.js +16 -0
- package/dist/memory/layout.js +75 -19
- package/package.json +1 -1
- package/scripts/install.sh +22 -0
|
@@ -19,7 +19,9 @@ const applyPatchSchema = Type.Object({
|
|
|
19
19
|
description: "Patch content using the *** Begin Patch/End Patch format.",
|
|
20
20
|
}),
|
|
21
21
|
});
|
|
22
|
-
export function createApplyPatchTool(options = {}
|
|
22
|
+
export function createApplyPatchTool(options = {}
|
|
23
|
+
// biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-agent-core uses a different module instance.
|
|
24
|
+
) {
|
|
23
25
|
const cwd = options.cwd ?? process.cwd();
|
|
24
26
|
const sandboxRoot = options.sandboxRoot;
|
|
25
27
|
return {
|
|
@@ -513,7 +513,9 @@ async function runExecProcess(opts) {
|
|
|
513
513
|
kill: () => killSession(session),
|
|
514
514
|
};
|
|
515
515
|
}
|
|
516
|
-
export function createExecTool(defaults
|
|
516
|
+
export function createExecTool(defaults
|
|
517
|
+
// biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-agent-core uses a different module instance.
|
|
518
|
+
) {
|
|
517
519
|
const defaultBackgroundMs = clampNumber(defaults?.backgroundMs ?? readEnvInt("PI_BASH_YIELD_MS"), 10_000, 10, 120_000);
|
|
518
520
|
const allowBackground = defaults?.allowBackground ?? true;
|
|
519
521
|
const defaultTimeoutSec = typeof defaults?.timeoutSec === "number" && defaults.timeoutSec > 0
|
|
@@ -15,7 +15,9 @@ const processSchema = Type.Object({
|
|
|
15
15
|
offset: Type.Optional(Type.Number({ description: "Log offset" })),
|
|
16
16
|
limit: Type.Optional(Type.Number({ description: "Log length" })),
|
|
17
17
|
});
|
|
18
|
-
export function createProcessTool(defaults
|
|
18
|
+
export function createProcessTool(defaults
|
|
19
|
+
// biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-agent-core uses a different module instance.
|
|
20
|
+
) {
|
|
19
21
|
if (defaults?.cleanupMs !== undefined) {
|
|
20
22
|
setJobTtlMs(defaults.cleanupMs);
|
|
21
23
|
}
|
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "../../config/config.js";
|
|
2
2
|
import { resolveTaskmasterPackageRoot } from "../../infra/taskmaster-root.js";
|
|
3
|
-
import { scheduleGatewaySigusr1Restart
|
|
3
|
+
import { scheduleGatewaySigusr1Restart } from "../../infra/restart.js";
|
|
4
4
|
import { formatDoctorNonInteractiveHint, readRestartSentinel, writeRestartSentinel, } from "../../infra/restart-sentinel.js";
|
|
5
5
|
import { checkUpdateStatus, compareSemverStrings } from "../../infra/update-check.js";
|
|
6
6
|
import { normalizeUpdateChannel, resolveEffectiveUpdateChannel, } from "../../infra/update-channels.js";
|
|
@@ -248,12 +248,13 @@ export const updateHandlers = {
|
|
|
248
248
|
// Non-critical — unit file just won't have watchdog directives
|
|
249
249
|
// until the next `daemon install --force`.
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
// Exit cleanly and let the supervisor (systemd Restart=always /
|
|
252
|
+
// launchd KeepAlive) restart us with the new code on disk.
|
|
253
|
+
// This avoids a race condition where spawnSync("systemctl restart")
|
|
254
|
+
// returns before our SIGTERM handler releases the port, causing the
|
|
255
|
+
// new process to fail with "port already in use" and crash-loop.
|
|
256
|
+
log.info("exiting for supervisor restart after global update");
|
|
257
|
+
process.exit(0);
|
|
257
258
|
})();
|
|
258
259
|
}, delayMs);
|
|
259
260
|
restart = { ok: true };
|
package/dist/memory/internal.js
CHANGED
|
@@ -34,6 +34,22 @@ async function walkDir(dir, files) {
|
|
|
34
34
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
35
35
|
for (const entry of entries) {
|
|
36
36
|
const full = path.join(dir, entry.name);
|
|
37
|
+
// Symlinks need fs.stat to determine the target type
|
|
38
|
+
if (entry.isSymbolicLink()) {
|
|
39
|
+
try {
|
|
40
|
+
const stat = await fs.stat(full);
|
|
41
|
+
if (stat.isDirectory()) {
|
|
42
|
+
await walkDir(full, files);
|
|
43
|
+
}
|
|
44
|
+
else if (stat.isFile() && entry.name.endsWith(".md")) {
|
|
45
|
+
files.push(full);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Broken symlink — skip
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
37
53
|
if (entry.isDirectory()) {
|
|
38
54
|
await walkDir(full, files);
|
|
39
55
|
continue;
|
package/dist/memory/layout.js
CHANGED
|
@@ -5,26 +5,30 @@
|
|
|
5
5
|
* ~/taskmaster/memory/ ← shared canonical location
|
|
6
6
|
* public/ ← shared: both agents read/write
|
|
7
7
|
* shared/ ← shared: both agents read/write
|
|
8
|
+
* users/ ← shared: both agents read/write (app-layer scoped)
|
|
9
|
+
* groups/ ← shared: both agents read/write (app-layer scoped)
|
|
8
10
|
* ~/taskmaster/agents/admin/memory/ ← physical directory
|
|
9
11
|
* admin/ ← physical: admin-only data
|
|
10
12
|
* public → ../../../memory/public (symlink to shared)
|
|
11
13
|
* shared → ../../../memory/shared (symlink to shared)
|
|
12
|
-
* users/
|
|
14
|
+
* users → ../../../memory/users (symlink to shared)
|
|
15
|
+
* groups → ../../../memory/groups (symlink to shared)
|
|
13
16
|
* ~/taskmaster/agents/public/memory/ ← physical directory
|
|
14
17
|
* public → ../../../memory/public (symlink to shared)
|
|
15
18
|
* shared → ../../../memory/shared (symlink to shared)
|
|
16
|
-
* users/
|
|
19
|
+
* users → ../../../memory/users (symlink to shared)
|
|
20
|
+
* groups → ../../../memory/groups (symlink to shared)
|
|
17
21
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
22
|
+
* public/, shared/, users/, and groups/ are symlinked — both agents share
|
|
23
|
+
* the same data. Access control is enforced by the application layer (scope config).
|
|
24
|
+
* admin/ only exists in the admin agent workspace.
|
|
21
25
|
*/
|
|
22
26
|
import fs from "node:fs/promises";
|
|
23
27
|
import path from "node:path";
|
|
24
28
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
25
29
|
const log = createSubsystemLogger("memory-layout");
|
|
26
30
|
/** Shared scope folders symlinked from each agent workspace. */
|
|
27
|
-
const SHARED_FOLDERS = ["public", "shared"];
|
|
31
|
+
const SHARED_FOLDERS = ["public", "shared", "users", "groups"];
|
|
28
32
|
/** The relative symlink target from agents/{id}/memory/{scope} → ../../../memory/{scope} */
|
|
29
33
|
const SYMLINK_TARGET_PREFIX = "../../../memory";
|
|
30
34
|
async function exists(p) {
|
|
@@ -81,14 +85,14 @@ async function mergeDir(src, dest) {
|
|
|
81
85
|
/**
|
|
82
86
|
* Ensure the correct memory layout for a multi-agent workspace.
|
|
83
87
|
*
|
|
84
|
-
* - Creates the shared
|
|
88
|
+
* - Creates the shared canonical directories at workspace root
|
|
85
89
|
* - For each agent dir, ensures `memory/` is a physical directory with
|
|
86
|
-
* symlinks for
|
|
90
|
+
* symlinks for shared folders and a physical `admin/` for the admin agent
|
|
87
91
|
* - Migrates from the old full-symlink layout if found
|
|
88
|
-
* - Consolidates data from
|
|
92
|
+
* - Consolidates data from physical dirs back into the shared location
|
|
89
93
|
*
|
|
90
94
|
* @param workspaceRoot The workspace root (e.g. ~/taskmaster)
|
|
91
|
-
* @param agentDirs Agent workspace directories
|
|
95
|
+
* @param agentDirs Agent workspace directories
|
|
92
96
|
*/
|
|
93
97
|
export async function ensureMemoryLayout(workspaceRoot, agentDirs) {
|
|
94
98
|
const sharedMemoryDir = path.join(workspaceRoot, "memory");
|
|
@@ -102,11 +106,8 @@ export async function ensureMemoryLayout(workspaceRoot, agentDirs) {
|
|
|
102
106
|
// Case 1: Full memory symlink (old layout) — migrate to per-subfolder
|
|
103
107
|
if (await isSymlink(memoryPath)) {
|
|
104
108
|
log.info(`migrating full memory symlink to per-subfolder layout: ${agentDir}`);
|
|
105
|
-
// The symlink points to the shared memory dir — data is already there.
|
|
106
|
-
// Remove the full symlink and create a physical dir with per-subfolder symlinks.
|
|
107
109
|
await fs.unlink(memoryPath);
|
|
108
110
|
await fs.mkdir(memoryPath, { recursive: true });
|
|
109
|
-
// Data is already in the shared location, just create symlinks and physical dirs
|
|
110
111
|
}
|
|
111
112
|
// Ensure memory/ is a physical directory
|
|
112
113
|
if (!(await isDirectory(memoryPath))) {
|
|
@@ -134,11 +135,10 @@ export async function ensureMemoryLayout(workspaceRoot, agentDirs) {
|
|
|
134
135
|
/* ignore if race */
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
|
-
//
|
|
138
|
+
// admin/ is the only per-agent physical directory — exists only in admin workspace
|
|
138
139
|
if (isAdmin) {
|
|
139
140
|
await fs.mkdir(path.join(memoryPath, "admin"), { recursive: true });
|
|
140
141
|
}
|
|
141
|
-
await fs.mkdir(path.join(memoryPath, "users"), { recursive: true });
|
|
142
142
|
// Remove admin/ from non-admin agent workspaces (it should never exist there)
|
|
143
143
|
if (!isAdmin) {
|
|
144
144
|
const adminPath = path.join(memoryPath, "admin");
|
|
@@ -147,13 +147,69 @@ export async function ensureMemoryLayout(workspaceRoot, agentDirs) {
|
|
|
147
147
|
await fs.rm(adminPath, { recursive: true }).catch(() => { });
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
|
-
log.warn(`non-admin agent has data in memory/admin/ — moving to
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
log.warn(`non-admin agent has data in memory/admin/ — moving to admin agent`);
|
|
151
|
+
const adminAgent = agentDirs.find((a) => a.id.toLowerCase() === "admin" || a.id.toLowerCase().endsWith("-admin"));
|
|
152
|
+
if (adminAgent) {
|
|
153
|
+
const adminMemAdmin = path.join(adminAgent.dir, "memory", "admin");
|
|
154
|
+
await fs.mkdir(adminMemAdmin, { recursive: true });
|
|
155
|
+
await mergeDir(adminPath, adminMemAdmin);
|
|
156
|
+
}
|
|
154
157
|
await fs.rm(adminPath, { recursive: true }).catch(() => { });
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
}
|
|
158
161
|
}
|
|
162
|
+
// Sweep orphaned admin-only data from the shared root.
|
|
163
|
+
// After full-symlink migration, admin/, notes/, and loose files may remain
|
|
164
|
+
// at the shared root. These belong in the admin agent's workspace.
|
|
165
|
+
await migrateOrphanedSharedData(sharedMemoryDir, agentDirs);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Move admin-only data that was left at the shared memory root
|
|
169
|
+
* (from the old full-symlink layout) into the admin agent's workspace.
|
|
170
|
+
*
|
|
171
|
+
* Shared folders (public, shared, users, groups) belong at the shared root.
|
|
172
|
+
* Everything else (admin, notes, loose .md files) is admin-only.
|
|
173
|
+
*/
|
|
174
|
+
async function migrateOrphanedSharedData(sharedMemoryDir, agentDirs) {
|
|
175
|
+
const adminAgent = agentDirs.find((a) => a.id.toLowerCase() === "admin" || a.id.toLowerCase().endsWith("-admin"));
|
|
176
|
+
if (!adminAgent)
|
|
177
|
+
return;
|
|
178
|
+
const adminMemoryPath = path.join(adminAgent.dir, "memory");
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = await fs.readdir(sharedMemoryDir);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const sharedSet = new Set(SHARED_FOLDERS);
|
|
187
|
+
for (const name of entries) {
|
|
188
|
+
// Skip shared folders — they belong here
|
|
189
|
+
if (sharedSet.has(name))
|
|
190
|
+
continue;
|
|
191
|
+
// Skip hidden files
|
|
192
|
+
if (name.startsWith("."))
|
|
193
|
+
continue;
|
|
194
|
+
const srcPath = path.join(sharedMemoryDir, name);
|
|
195
|
+
if (await isDirectory(srcPath)) {
|
|
196
|
+
const destPath = path.join(adminMemoryPath, name);
|
|
197
|
+
if (await isEmptyDir(srcPath)) {
|
|
198
|
+
await fs.rm(srcPath, { recursive: true }).catch(() => { });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
log.info(`migrating orphaned ${name}/ from shared root to admin agent`);
|
|
202
|
+
await mergeDir(srcPath, destPath);
|
|
203
|
+
if (await isEmptyDir(srcPath)) {
|
|
204
|
+
await fs.rm(srcPath, { recursive: true }).catch(() => { });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if (name.endsWith(".md")) {
|
|
208
|
+
const destPath = path.join(adminMemoryPath, name);
|
|
209
|
+
if (!(await exists(destPath))) {
|
|
210
|
+
log.info(`migrating orphaned ${name} from shared root to admin agent`);
|
|
211
|
+
await fs.rename(srcPath, destPath);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
159
215
|
}
|
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -105,6 +105,28 @@ if ! check_node; then
|
|
|
105
105
|
install_node
|
|
106
106
|
fi
|
|
107
107
|
|
|
108
|
+
# ── Stop existing daemon before upgrade ──────────────────────────────────────
|
|
109
|
+
# The old gateway process holds the port. It must be stopped before npm
|
|
110
|
+
# replaces the files on disk, otherwise the new daemon cannot bind.
|
|
111
|
+
|
|
112
|
+
if command -v taskmaster >/dev/null 2>&1; then
|
|
113
|
+
echo ""
|
|
114
|
+
echo "Stopping existing gateway daemon..."
|
|
115
|
+
if [ "$PLATFORM" = "linux" ] && [ "$(id -u)" = "0" ] && [ "${SUDO_USER:-}" != "" ]; then
|
|
116
|
+
# Running as root via sudo — must run as the real user for systemctl --user
|
|
117
|
+
_STOP_USER="${SUDO_USER}"
|
|
118
|
+
_STOP_UID=$(id -u "$_STOP_USER")
|
|
119
|
+
sudo -u "$_STOP_USER" \
|
|
120
|
+
XDG_RUNTIME_DIR="/run/user/$_STOP_UID" \
|
|
121
|
+
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$_STOP_UID/bus" \
|
|
122
|
+
taskmaster gateway stop 2>/dev/null || true
|
|
123
|
+
else
|
|
124
|
+
taskmaster gateway stop 2>/dev/null || true
|
|
125
|
+
fi
|
|
126
|
+
# Give the process a moment to release the port
|
|
127
|
+
sleep 2
|
|
128
|
+
fi
|
|
129
|
+
|
|
108
130
|
# ── Install Taskmaster via npm ───────────────────────────────────────────────
|
|
109
131
|
|
|
110
132
|
echo ""
|