zam-core 0.4.0 → 0.4.2
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/.agents/skills/zam/SKILL.md +1 -1
- package/README.de.md +19 -0
- package/README.md +19 -0
- package/dist/cli/index.js +501 -90
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { readFileSync as readFileSync14 } from "fs";
|
|
5
|
-
import { dirname as dirname10, join as
|
|
5
|
+
import { dirname as dirname10, join as join23 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7
7
|
import { Command as Command24 } from "commander";
|
|
8
8
|
|
|
@@ -4847,6 +4847,37 @@ function decideUpdate(input8) {
|
|
|
4847
4847
|
};
|
|
4848
4848
|
}
|
|
4849
4849
|
}
|
|
4850
|
+
function planUpdate(decision) {
|
|
4851
|
+
if (!decision.updateAvailable) return [];
|
|
4852
|
+
switch (decision.channel) {
|
|
4853
|
+
case "developer":
|
|
4854
|
+
return [
|
|
4855
|
+
{ kind: "git-pull", label: "Pull the latest source" },
|
|
4856
|
+
{ kind: "npm-install", label: "Install dependencies" },
|
|
4857
|
+
{ kind: "npm-build", label: "Rebuild the CLI" },
|
|
4858
|
+
{
|
|
4859
|
+
kind: "distribute-skills",
|
|
4860
|
+
label: "Refresh skill files (zam setup --force)"
|
|
4861
|
+
}
|
|
4862
|
+
];
|
|
4863
|
+
case "winget":
|
|
4864
|
+
case "homebrew":
|
|
4865
|
+
return [
|
|
4866
|
+
{
|
|
4867
|
+
kind: "run-command",
|
|
4868
|
+
label: `Update via ${decision.channel}`,
|
|
4869
|
+
command: decision.command
|
|
4870
|
+
}
|
|
4871
|
+
];
|
|
4872
|
+
case "direct":
|
|
4873
|
+
return [
|
|
4874
|
+
{
|
|
4875
|
+
kind: "self-update",
|
|
4876
|
+
label: "Apply the signed update via the desktop app"
|
|
4877
|
+
}
|
|
4878
|
+
];
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4850
4881
|
|
|
4851
4882
|
// src/cli/commands/agent.ts
|
|
4852
4883
|
var C = {
|
|
@@ -4901,10 +4932,10 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
4901
4932
|
|
|
4902
4933
|
// src/cli/commands/bridge.ts
|
|
4903
4934
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
4904
|
-
import { randomBytes } from "crypto";
|
|
4935
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
4905
4936
|
import { readdirSync as readdirSync2, readFileSync as readFileSync10, rmSync as rmSync2 } from "fs";
|
|
4906
|
-
import { homedir as homedir8, tmpdir } from "os";
|
|
4907
|
-
import { join as
|
|
4937
|
+
import { homedir as homedir8, tmpdir as tmpdir2 } from "os";
|
|
4938
|
+
import { join as join13 } from "path";
|
|
4908
4939
|
import { Command as Command2 } from "commander";
|
|
4909
4940
|
|
|
4910
4941
|
// src/cli/llm/client.ts
|
|
@@ -5267,8 +5298,8 @@ async function startLocalRunner(url, model, locale) {
|
|
|
5267
5298
|
process.stdout.write("\n");
|
|
5268
5299
|
console.log(`\x1B[33m${t(locale, "wait_warning")}\x1B[0m`);
|
|
5269
5300
|
console.log(`\x1B[2m${t(locale, "wait_info")}\x1B[0m`);
|
|
5270
|
-
const { confirm:
|
|
5271
|
-
const keepWaiting = await
|
|
5301
|
+
const { confirm: confirm5 } = await import("@inquirer/prompts");
|
|
5302
|
+
const keepWaiting = await confirm5({
|
|
5272
5303
|
message: t(locale, "keep_waiting_llm"),
|
|
5273
5304
|
default: true
|
|
5274
5305
|
}).catch(() => false);
|
|
@@ -5412,8 +5443,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
5412
5443
|
}
|
|
5413
5444
|
console.log(`
|
|
5414
5445
|
\x1B[33m${t(locale, "local_ai_working")}\x1B[0m`);
|
|
5415
|
-
const { confirm:
|
|
5416
|
-
const keepWaiting = await
|
|
5446
|
+
const { confirm: confirm5 } = await import("@inquirer/prompts");
|
|
5447
|
+
const keepWaiting = await confirm5({
|
|
5417
5448
|
message: t(locale, "keep_waiting"),
|
|
5418
5449
|
default: true
|
|
5419
5450
|
}).catch(() => false);
|
|
@@ -5467,7 +5498,9 @@ async function ensureHighQualityQuestion(db, token) {
|
|
|
5467
5498
|
|
|
5468
5499
|
// src/cli/llm/vision.ts
|
|
5469
5500
|
import { readFileSync as readFileSync9 } from "fs";
|
|
5470
|
-
import {
|
|
5501
|
+
import { randomBytes } from "crypto";
|
|
5502
|
+
import { tmpdir } from "os";
|
|
5503
|
+
import { basename as basename2, join as join12 } from "path";
|
|
5471
5504
|
var LANGUAGE_NAMES2 = {
|
|
5472
5505
|
en: "English",
|
|
5473
5506
|
de: "German",
|
|
@@ -5500,15 +5533,61 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
5500
5533
|
"Vision observation is disabled in settings (llm.vision.enabled)"
|
|
5501
5534
|
);
|
|
5502
5535
|
}
|
|
5503
|
-
const
|
|
5504
|
-
const
|
|
5536
|
+
const imageUrls = [];
|
|
5537
|
+
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
5538
|
+
if (isVideo) {
|
|
5539
|
+
const { mkdirSync: mkdirSync12, readdirSync: readdirSync3, rmSync: rmSync3 } = await import("fs");
|
|
5540
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
5541
|
+
const tempDir = join12(tmpdir(), `zam-frames-${randomBytes(4).toString("hex")}`);
|
|
5542
|
+
mkdirSync12(tempDir, { recursive: true });
|
|
5543
|
+
try {
|
|
5544
|
+
execSync7(
|
|
5545
|
+
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
5546
|
+
{ stdio: "ignore" }
|
|
5547
|
+
);
|
|
5548
|
+
let files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
5549
|
+
if (files.length === 0) {
|
|
5550
|
+
execSync7(
|
|
5551
|
+
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
5552
|
+
{ stdio: "ignore" }
|
|
5553
|
+
);
|
|
5554
|
+
files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
5555
|
+
}
|
|
5556
|
+
let sampledFiles = files;
|
|
5557
|
+
if (files.length > 12) {
|
|
5558
|
+
const step = (files.length - 1) / 11;
|
|
5559
|
+
sampledFiles = [];
|
|
5560
|
+
for (let i = 0; i < 12; i++) {
|
|
5561
|
+
const index = Math.round(i * step);
|
|
5562
|
+
sampledFiles.push(files[index]);
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
for (const file of sampledFiles) {
|
|
5566
|
+
const bytes = readFileSync9(join12(tempDir, file));
|
|
5567
|
+
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
5568
|
+
}
|
|
5569
|
+
} finally {
|
|
5570
|
+
try {
|
|
5571
|
+
rmSync3(tempDir, { recursive: true, force: true });
|
|
5572
|
+
} catch {
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5575
|
+
} else {
|
|
5576
|
+
const imageBytes = readFileSync9(input8.imagePath);
|
|
5577
|
+
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
5578
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
5579
|
+
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
5580
|
+
}
|
|
5581
|
+
if (imageUrls.length === 0) {
|
|
5582
|
+
throw new Error("No image data available for vision analysis");
|
|
5583
|
+
}
|
|
5505
5584
|
const model = input8.model ?? cfg.model;
|
|
5506
5585
|
const content = await requestVisionDraft({
|
|
5507
5586
|
url: cfg.url,
|
|
5508
5587
|
apiKey: cfg.apiKey || DEFAULT_LLM_API_KEY,
|
|
5509
5588
|
model,
|
|
5510
5589
|
locale: cfg.locale,
|
|
5511
|
-
|
|
5590
|
+
imageUrls,
|
|
5512
5591
|
input: input8
|
|
5513
5592
|
});
|
|
5514
5593
|
let draft;
|
|
@@ -5558,17 +5637,22 @@ async function requestVisionDraft(args) {
|
|
|
5558
5637
|
content: [
|
|
5559
5638
|
{
|
|
5560
5639
|
type: "text",
|
|
5561
|
-
text: `Observe this Windows application
|
|
5640
|
+
text: args.imageUrls.length > 1 ? `Observe this sequence of Windows/macOS application snapshots showing a task performed over time.
|
|
5641
|
+
Application process: ${args.input.application.processName}
|
|
5642
|
+
Window title: ${args.input.application.windowTitle ?? "(unknown)"}
|
|
5643
|
+
|
|
5644
|
+
Return this JSON draft only:
|
|
5645
|
+
${schema}` : `Observe this Windows/macOS application snapshot for a learning session.
|
|
5562
5646
|
Application process: ${args.input.application.processName}
|
|
5563
5647
|
Window title: ${args.input.application.windowTitle ?? "(unknown)"}
|
|
5564
5648
|
|
|
5565
5649
|
Return this JSON draft only:
|
|
5566
5650
|
${schema}`
|
|
5567
5651
|
},
|
|
5568
|
-
{
|
|
5652
|
+
...args.imageUrls.map((url) => ({
|
|
5569
5653
|
type: "image_url",
|
|
5570
|
-
image_url: { url
|
|
5571
|
-
}
|
|
5654
|
+
image_url: { url }
|
|
5655
|
+
}))
|
|
5572
5656
|
]
|
|
5573
5657
|
}
|
|
5574
5658
|
],
|
|
@@ -6208,7 +6292,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
6208
6292
|
"20"
|
|
6209
6293
|
).action(async (opts) => {
|
|
6210
6294
|
try {
|
|
6211
|
-
const monitorDir =
|
|
6295
|
+
const monitorDir = join13(homedir8(), ".zam", "monitor");
|
|
6212
6296
|
let files;
|
|
6213
6297
|
try {
|
|
6214
6298
|
files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -6221,7 +6305,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
6221
6305
|
return;
|
|
6222
6306
|
}
|
|
6223
6307
|
const limit = Number(opts.limit);
|
|
6224
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
6308
|
+
const sorted = files.map((f) => ({ name: f, path: join13(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
6225
6309
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
6226
6310
|
for (const file of sorted) {
|
|
6227
6311
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -6301,8 +6385,8 @@ bridgeCommand.command("get-observations").description("Read UI observer reports
|
|
|
6301
6385
|
}
|
|
6302
6386
|
});
|
|
6303
6387
|
bridgeCommand.command("observe-ui-snapshot").description(
|
|
6304
|
-
"Analyze a captured UI snapshot with the configured vision LLM (JSON)"
|
|
6305
|
-
).requiredOption("--session <id>", "Observer session ID").requiredOption("--sequence <n>", "Monotonic observation sequence number").requiredOption("--image <path>", "PNG snapshot path").requiredOption("--observed-from <iso>", "Observation window start time").requiredOption("--observed-to <iso>", "Observation window end time").requiredOption("--process-name <name>", "Observed application process name").option("--process-id <n>", "Observed application process ID").option("--window-title <title>", "Observed window title").option("--evidence-ref <ref>", "Evidence reference to put in the report").option("--model <model>", "Override configured LLM model for this request").option("--max-tokens <n>", "Model response token budget").option("--timeout <ms>", "Hard request timeout in milliseconds").option("--redacted", "Mark the snapshot evidence as redacted").option("--write-log", "Append the generated report to the session JSONL").action(async (opts) => {
|
|
6388
|
+
"Analyze a captured UI snapshot or video recording with the configured vision LLM (JSON)"
|
|
6389
|
+
).requiredOption("--session <id>", "Observer session ID").requiredOption("--sequence <n>", "Monotonic observation sequence number").requiredOption("--image <path>", "PNG snapshot or video recording path").requiredOption("--observed-from <iso>", "Observation window start time").requiredOption("--observed-to <iso>", "Observation window end time").requiredOption("--process-name <name>", "Observed application process name").option("--process-id <n>", "Observed application process ID").option("--window-title <title>", "Observed window title").option("--evidence-ref <ref>", "Evidence reference to put in the report").option("--model <model>", "Override configured LLM model for this request").option("--max-tokens <n>", "Model response token budget").option("--timeout <ms>", "Hard request timeout in milliseconds").option("--redacted", "Mark the snapshot evidence as redacted").option("--write-log", "Append the generated report to the session JSONL").action(async (opts) => {
|
|
6306
6390
|
const sequence = parseNonNegativeIntegerOption("sequence", opts.sequence);
|
|
6307
6391
|
const processId = opts.processId === void 0 ? void 0 : parseNonNegativeIntegerOption("process-id", opts.processId);
|
|
6308
6392
|
const maxTokens = opts.maxTokens === void 0 ? void 0 : parseNonNegativeIntegerOption("max-tokens", opts.maxTokens);
|
|
@@ -6723,7 +6807,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
6723
6807
|
return;
|
|
6724
6808
|
}
|
|
6725
6809
|
}
|
|
6726
|
-
const outputPath = opts.image ?? opts.output ??
|
|
6810
|
+
const outputPath = opts.image ?? opts.output ?? join13(tmpdir2(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
6727
6811
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
6728
6812
|
if (!isProvided) {
|
|
6729
6813
|
const post = decidePostCapture(policy, {
|
|
@@ -6767,6 +6851,177 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
6767
6851
|
});
|
|
6768
6852
|
});
|
|
6769
6853
|
});
|
|
6854
|
+
bridgeCommand.command("start-recording").description("Start screen recording in the background (macOS only) (JSON)").requiredOption("--session <id>", "ZAM session ID").option("--output <path>", "Video output path").action(async (opts) => {
|
|
6855
|
+
const platform = process.platform;
|
|
6856
|
+
if (platform !== "darwin") {
|
|
6857
|
+
jsonOut2({
|
|
6858
|
+
sessionId: opts.session,
|
|
6859
|
+
started: false,
|
|
6860
|
+
error: "Screen recording is only supported on macOS (darwin)"
|
|
6861
|
+
});
|
|
6862
|
+
return;
|
|
6863
|
+
}
|
|
6864
|
+
const sessionId = opts.session;
|
|
6865
|
+
const statePath = join13(tmpdir2(), `zam-recording-${sessionId}.json`);
|
|
6866
|
+
const outputPath = opts.output ?? join13(tmpdir2(), `zam-recording-${sessionId}.mov`);
|
|
6867
|
+
const { existsSync: existsSync22, writeFileSync: writeFileSync12, openSync, closeSync } = await import("fs");
|
|
6868
|
+
if (existsSync22(statePath)) {
|
|
6869
|
+
jsonOut2({
|
|
6870
|
+
sessionId,
|
|
6871
|
+
started: false,
|
|
6872
|
+
error: `Recording is already active for session ${sessionId}`
|
|
6873
|
+
});
|
|
6874
|
+
return;
|
|
6875
|
+
}
|
|
6876
|
+
const logPath = join13(tmpdir2(), `zam-recording-${sessionId}.log`);
|
|
6877
|
+
let logFd;
|
|
6878
|
+
try {
|
|
6879
|
+
logFd = openSync(logPath, "w");
|
|
6880
|
+
} catch (e) {
|
|
6881
|
+
jsonOut2({
|
|
6882
|
+
sessionId,
|
|
6883
|
+
started: false,
|
|
6884
|
+
error: `Failed to open log file at ${logPath}: ${e.message}`
|
|
6885
|
+
});
|
|
6886
|
+
return;
|
|
6887
|
+
}
|
|
6888
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
6889
|
+
const child = spawn3(
|
|
6890
|
+
"ffmpeg",
|
|
6891
|
+
[
|
|
6892
|
+
"-y",
|
|
6893
|
+
"-f",
|
|
6894
|
+
"avfoundation",
|
|
6895
|
+
"-r",
|
|
6896
|
+
"5",
|
|
6897
|
+
"-i",
|
|
6898
|
+
"0",
|
|
6899
|
+
"-pix_fmt",
|
|
6900
|
+
"yuv420p",
|
|
6901
|
+
outputPath
|
|
6902
|
+
],
|
|
6903
|
+
{
|
|
6904
|
+
detached: true,
|
|
6905
|
+
stdio: ["pipe", logFd, logFd]
|
|
6906
|
+
}
|
|
6907
|
+
);
|
|
6908
|
+
try {
|
|
6909
|
+
closeSync(logFd);
|
|
6910
|
+
} catch {
|
|
6911
|
+
}
|
|
6912
|
+
child.unref();
|
|
6913
|
+
if (child.pid) {
|
|
6914
|
+
writeFileSync12(
|
|
6915
|
+
statePath,
|
|
6916
|
+
JSON.stringify({
|
|
6917
|
+
pid: child.pid,
|
|
6918
|
+
outputPath,
|
|
6919
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6920
|
+
}),
|
|
6921
|
+
"utf8"
|
|
6922
|
+
);
|
|
6923
|
+
jsonOut2({
|
|
6924
|
+
sessionId,
|
|
6925
|
+
started: true,
|
|
6926
|
+
outputPath,
|
|
6927
|
+
pid: child.pid
|
|
6928
|
+
});
|
|
6929
|
+
} else {
|
|
6930
|
+
jsonOut2({
|
|
6931
|
+
sessionId,
|
|
6932
|
+
started: false,
|
|
6933
|
+
error: "Failed to spawn ffmpeg process"
|
|
6934
|
+
});
|
|
6935
|
+
}
|
|
6936
|
+
});
|
|
6937
|
+
bridgeCommand.command("stop-recording").description(
|
|
6938
|
+
"Stop active screen recording and apply idle-frame compression (macOS only) (JSON)"
|
|
6939
|
+
).requiredOption("--session <id>", "ZAM session ID").action(async (opts) => {
|
|
6940
|
+
const platform = process.platform;
|
|
6941
|
+
if (platform !== "darwin") {
|
|
6942
|
+
jsonOut2({
|
|
6943
|
+
sessionId: opts.session,
|
|
6944
|
+
stopped: false,
|
|
6945
|
+
error: "Screen recording is only supported on macOS (darwin)"
|
|
6946
|
+
});
|
|
6947
|
+
return;
|
|
6948
|
+
}
|
|
6949
|
+
const sessionId = opts.session;
|
|
6950
|
+
const statePath = join13(tmpdir2(), `zam-recording-${sessionId}.json`);
|
|
6951
|
+
const { existsSync: existsSync22, readFileSync: readFileSync15, rmSync: rmSync3 } = await import("fs");
|
|
6952
|
+
if (!existsSync22(statePath)) {
|
|
6953
|
+
jsonOut2({
|
|
6954
|
+
sessionId,
|
|
6955
|
+
stopped: false,
|
|
6956
|
+
error: `No active recording found for session ${sessionId}`
|
|
6957
|
+
});
|
|
6958
|
+
return;
|
|
6959
|
+
}
|
|
6960
|
+
const state = JSON.parse(readFileSync15(statePath, "utf8"));
|
|
6961
|
+
const { pid, outputPath } = state;
|
|
6962
|
+
try {
|
|
6963
|
+
process.kill(pid, "SIGINT");
|
|
6964
|
+
} catch (e) {
|
|
6965
|
+
}
|
|
6966
|
+
const isProcessRunning = (pId) => {
|
|
6967
|
+
try {
|
|
6968
|
+
process.kill(pId, 0);
|
|
6969
|
+
return true;
|
|
6970
|
+
} catch (e) {
|
|
6971
|
+
return false;
|
|
6972
|
+
}
|
|
6973
|
+
};
|
|
6974
|
+
let attempts = 0;
|
|
6975
|
+
while (isProcessRunning(pid) && attempts < 20) {
|
|
6976
|
+
await new Promise((resolve5) => setTimeout(resolve5, 250));
|
|
6977
|
+
attempts++;
|
|
6978
|
+
}
|
|
6979
|
+
if (isProcessRunning(pid)) {
|
|
6980
|
+
try {
|
|
6981
|
+
process.kill(pid, "SIGKILL");
|
|
6982
|
+
} catch (e) {
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
try {
|
|
6986
|
+
rmSync3(statePath, { force: true });
|
|
6987
|
+
} catch {
|
|
6988
|
+
}
|
|
6989
|
+
if (!existsSync22(outputPath)) {
|
|
6990
|
+
jsonOut2({
|
|
6991
|
+
sessionId,
|
|
6992
|
+
stopped: false,
|
|
6993
|
+
error: `Recording file not found at ${outputPath}`
|
|
6994
|
+
});
|
|
6995
|
+
return;
|
|
6996
|
+
}
|
|
6997
|
+
const decimatedPath = outputPath.replace(/\.[^.]+$/, "-decimated.mp4");
|
|
6998
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
6999
|
+
try {
|
|
7000
|
+
execSync7(
|
|
7001
|
+
`ffmpeg -y -i "${outputPath}" -vf "mpdecimate,setpts=N/FRAME_RATE/TB" -an -pix_fmt yuv420p "${decimatedPath}"`,
|
|
7002
|
+
{ stdio: "ignore" }
|
|
7003
|
+
);
|
|
7004
|
+
} catch (ffmpegErr) {
|
|
7005
|
+
jsonOut2({
|
|
7006
|
+
sessionId,
|
|
7007
|
+
stopped: true,
|
|
7008
|
+
videoPath: outputPath,
|
|
7009
|
+
decimated: false,
|
|
7010
|
+
warning: `mpdecimate post-processing failed: ${ffmpegErr.message}`
|
|
7011
|
+
});
|
|
7012
|
+
return;
|
|
7013
|
+
}
|
|
7014
|
+
try {
|
|
7015
|
+
rmSync3(outputPath, { force: true });
|
|
7016
|
+
} catch {
|
|
7017
|
+
}
|
|
7018
|
+
jsonOut2({
|
|
7019
|
+
sessionId,
|
|
7020
|
+
stopped: true,
|
|
7021
|
+
videoPath: decimatedPath,
|
|
7022
|
+
decimated: true
|
|
7023
|
+
});
|
|
7024
|
+
});
|
|
6770
7025
|
bridgeCommand.command("get-observer-policy").description(
|
|
6771
7026
|
"Report the resolved observer policy so an agent can check before capturing (JSON)"
|
|
6772
7027
|
).action(async () => {
|
|
@@ -7475,18 +7730,18 @@ async function setupTurso(urlArg, tokenArg, mode) {
|
|
|
7475
7730
|
// src/cli/commands/git-sync.ts
|
|
7476
7731
|
import { execSync as execSync4 } from "child_process";
|
|
7477
7732
|
import { chmodSync as chmodSync2, existsSync as existsSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
7478
|
-
import { join as
|
|
7733
|
+
import { join as join14 } from "path";
|
|
7479
7734
|
import { Command as Command5 } from "commander";
|
|
7480
7735
|
function installHook2() {
|
|
7481
|
-
const gitDir =
|
|
7736
|
+
const gitDir = join14(process.cwd(), ".git");
|
|
7482
7737
|
if (!existsSync13(gitDir)) {
|
|
7483
7738
|
console.error(
|
|
7484
7739
|
"Error: Current directory is not the root of a Git repository."
|
|
7485
7740
|
);
|
|
7486
7741
|
process.exit(1);
|
|
7487
7742
|
}
|
|
7488
|
-
const hooksDir =
|
|
7489
|
-
const hookPath =
|
|
7743
|
+
const hooksDir = join14(gitDir, "hooks");
|
|
7744
|
+
const hookPath = join14(hooksDir, "post-commit");
|
|
7490
7745
|
const hookContent = `#!/bin/sh
|
|
7491
7746
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
7492
7747
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
@@ -7782,7 +8037,7 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
7782
8037
|
// src/cli/commands/init.ts
|
|
7783
8038
|
import { existsSync as existsSync15, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
7784
8039
|
import { homedir as homedir9 } from "os";
|
|
7785
|
-
import { join as
|
|
8040
|
+
import { join as join15 } from "path";
|
|
7786
8041
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
7787
8042
|
import { Command as Command7 } from "commander";
|
|
7788
8043
|
var HOME2 = homedir9();
|
|
@@ -7790,10 +8045,10 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
|
7790
8045
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
7791
8046
|
}
|
|
7792
8047
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
7793
|
-
mkdirSync9(
|
|
7794
|
-
mkdirSync9(
|
|
7795
|
-
mkdirSync9(
|
|
7796
|
-
const worldviewFile =
|
|
8048
|
+
mkdirSync9(join15(workspaceDir, "beliefs"), { recursive: true });
|
|
8049
|
+
mkdirSync9(join15(workspaceDir, "goals"), { recursive: true });
|
|
8050
|
+
mkdirSync9(join15(workspaceDir, "skills"), { recursive: true });
|
|
8051
|
+
const worldviewFile = join15(workspaceDir, "beliefs", "worldview.md");
|
|
7797
8052
|
if (!existsSync15(worldviewFile)) {
|
|
7798
8053
|
writeFileSync7(
|
|
7799
8054
|
worldviewFile,
|
|
@@ -7807,7 +8062,7 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
7807
8062
|
"utf8"
|
|
7808
8063
|
);
|
|
7809
8064
|
}
|
|
7810
|
-
const goalsFile =
|
|
8065
|
+
const goalsFile = join15(workspaceDir, "goals", "goals.md");
|
|
7811
8066
|
if (!existsSync15(goalsFile)) {
|
|
7812
8067
|
writeFileSync7(
|
|
7813
8068
|
goalsFile,
|
|
@@ -7831,7 +8086,7 @@ var initCommand = new Command7("init").description("Launch the guided interactiv
|
|
|
7831
8086
|
);
|
|
7832
8087
|
printLine();
|
|
7833
8088
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
7834
|
-
const defaultWorkspace =
|
|
8089
|
+
const defaultWorkspace = join15(HOME2, "Documents", "zam");
|
|
7835
8090
|
const workspacePath = await input3({
|
|
7836
8091
|
message: "Choose your ZAM workspace directory:",
|
|
7837
8092
|
default: defaultWorkspace
|
|
@@ -8530,8 +8785,8 @@ ${"\u2550".repeat(50)}`);
|
|
|
8530
8785
|
// src/cli/commands/monitor.ts
|
|
8531
8786
|
import { execFileSync as execFileSync3, execSync as execSync5 } from "child_process";
|
|
8532
8787
|
import { unlinkSync, writeFileSync as writeFileSync8 } from "fs";
|
|
8533
|
-
import { tmpdir as
|
|
8534
|
-
import { basename as basename3, join as
|
|
8788
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
8789
|
+
import { basename as basename3, join as join16 } from "path";
|
|
8535
8790
|
import { Command as Command9 } from "commander";
|
|
8536
8791
|
function isPowerShellShell(shell) {
|
|
8537
8792
|
return shell === "pwsh" || shell === "powershell";
|
|
@@ -8708,8 +8963,8 @@ function resolveZamInvocation(shell) {
|
|
|
8708
8963
|
if (installed) {
|
|
8709
8964
|
return isPowerShellShell(shell) ? `& ${psSingleQuoted2(installed)}` : installed;
|
|
8710
8965
|
}
|
|
8711
|
-
const projectRoot =
|
|
8712
|
-
const cliSource =
|
|
8966
|
+
const projectRoot = join16(import.meta.dirname, "..", "..", "..");
|
|
8967
|
+
const cliSource = join16(projectRoot, "src/cli/index.ts");
|
|
8713
8968
|
if (isPowerShellShell(shell)) {
|
|
8714
8969
|
return `& npx --prefix ${psSingleQuoted2(projectRoot)} tsx ${psSingleQuoted2(cliSource)}`;
|
|
8715
8970
|
}
|
|
@@ -8799,7 +9054,7 @@ end tell` : `tell application "Terminal"
|
|
|
8799
9054
|
activate
|
|
8800
9055
|
do script "${escaped}"
|
|
8801
9056
|
end tell`;
|
|
8802
|
-
const tmpFile =
|
|
9057
|
+
const tmpFile = join16(tmpdir3(), `zam-monitor-${sessionId}.scpt`);
|
|
8803
9058
|
try {
|
|
8804
9059
|
writeFileSync8(tmpFile, appleScript);
|
|
8805
9060
|
execSync5(`osascript ${JSON.stringify(tmpFile)}`, { stdio: "ignore" });
|
|
@@ -8915,7 +9170,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
8915
9170
|
|
|
8916
9171
|
// src/cli/commands/profile.ts
|
|
8917
9172
|
import { homedir as homedir10 } from "os";
|
|
8918
|
-
import { dirname as dirname5, join as
|
|
9173
|
+
import { dirname as dirname5, join as join17, resolve as resolve4 } from "path";
|
|
8919
9174
|
import { Command as Command11 } from "commander";
|
|
8920
9175
|
var C2 = {
|
|
8921
9176
|
reset: "\x1B[0m",
|
|
@@ -8925,7 +9180,7 @@ var C2 = {
|
|
|
8925
9180
|
green: "\x1B[32m"
|
|
8926
9181
|
};
|
|
8927
9182
|
function defaultPersonalDir() {
|
|
8928
|
-
return
|
|
9183
|
+
return join17(homedir10(), "Documents", "zam");
|
|
8929
9184
|
}
|
|
8930
9185
|
function render(profile) {
|
|
8931
9186
|
const sync = profile.syncProvider ? `${C2.green}${profile.syncProvider}${C2.reset} ${C2.dim}(good for cross-device snapshots)${C2.reset}` : `${C2.dim}local folder (use a synced folder or snapshots to move between machines)${C2.reset}`;
|
|
@@ -9646,31 +9901,31 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
9646
9901
|
|
|
9647
9902
|
// src/cli/commands/setup.ts
|
|
9648
9903
|
import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
9649
|
-
import { basename as basename4, dirname as dirname6, join as
|
|
9904
|
+
import { basename as basename4, dirname as dirname6, join as join18 } from "path";
|
|
9650
9905
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9651
9906
|
import { Command as Command15 } from "commander";
|
|
9652
9907
|
var packageRoot = [
|
|
9653
9908
|
fileURLToPath2(new URL("../..", import.meta.url)),
|
|
9654
9909
|
fileURLToPath2(new URL("../../..", import.meta.url))
|
|
9655
|
-
].find((candidate) => existsSync17(
|
|
9910
|
+
].find((candidate) => existsSync17(join18(candidate, "package.json"))) ?? fileURLToPath2(new URL("../..", import.meta.url));
|
|
9656
9911
|
var SKILL_PAIRS = [
|
|
9657
9912
|
{
|
|
9658
|
-
from:
|
|
9659
|
-
to:
|
|
9913
|
+
from: join18(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
|
|
9914
|
+
to: join18(".claude", "skills", "zam", "SKILL.md")
|
|
9660
9915
|
},
|
|
9661
9916
|
{
|
|
9662
|
-
from:
|
|
9663
|
-
to:
|
|
9917
|
+
from: join18(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
|
|
9918
|
+
to: join18(".agent", "skills", "zam", "SKILL.md")
|
|
9664
9919
|
},
|
|
9665
9920
|
{
|
|
9666
|
-
from:
|
|
9667
|
-
to:
|
|
9921
|
+
from: join18(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
|
|
9922
|
+
to: join18(".agents", "skills", "zam", "SKILL.md")
|
|
9668
9923
|
}
|
|
9669
9924
|
];
|
|
9670
9925
|
function copySkills(force, cwd = process.cwd()) {
|
|
9671
9926
|
let anyAction = false;
|
|
9672
9927
|
for (const { from, to } of SKILL_PAIRS) {
|
|
9673
|
-
const dest =
|
|
9928
|
+
const dest = join18(cwd, to);
|
|
9674
9929
|
if (!existsSync17(from)) {
|
|
9675
9930
|
console.warn(` warn source not found, skipping: ${from}`);
|
|
9676
9931
|
continue;
|
|
@@ -9720,7 +9975,7 @@ async function initDatabase(skipInit) {
|
|
|
9720
9975
|
}
|
|
9721
9976
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd()) {
|
|
9722
9977
|
if (skipClaudeMd) return;
|
|
9723
|
-
const dest =
|
|
9978
|
+
const dest = join18(cwd, "CLAUDE.md");
|
|
9724
9979
|
if (existsSync17(dest)) {
|
|
9725
9980
|
console.log(` skip CLAUDE.md (already present)`);
|
|
9726
9981
|
return;
|
|
@@ -9754,7 +10009,7 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
9754
10009
|
}
|
|
9755
10010
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd()) {
|
|
9756
10011
|
if (skipAgentsMd) return;
|
|
9757
|
-
const dest =
|
|
10012
|
+
const dest = join18(cwd, "AGENTS.md");
|
|
9758
10013
|
if (existsSync17(dest)) {
|
|
9759
10014
|
console.log(` skip AGENTS.md (already present)`);
|
|
9760
10015
|
return;
|
|
@@ -9902,7 +10157,7 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
9902
10157
|
// src/cli/commands/snapshot.ts
|
|
9903
10158
|
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
9904
10159
|
import { homedir as homedir11 } from "os";
|
|
9905
|
-
import { dirname as dirname7, join as
|
|
10160
|
+
import { dirname as dirname7, join as join19 } from "path";
|
|
9906
10161
|
import { Command as Command17 } from "commander";
|
|
9907
10162
|
function defaultOutName() {
|
|
9908
10163
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -9922,7 +10177,7 @@ var exportCmd = new Command17("export").description("Write a portable SQL-text s
|
|
|
9922
10177
|
try {
|
|
9923
10178
|
db = await openDatabaseWithSync({ initialize: true });
|
|
9924
10179
|
const snapshot = await exportSnapshot(db);
|
|
9925
|
-
const personalDir = await getSetting(db, "personal.workspace_dir") ||
|
|
10180
|
+
const personalDir = await getSetting(db, "personal.workspace_dir") || join19(homedir11(), "Documents", "zam");
|
|
9926
10181
|
await db.close();
|
|
9927
10182
|
db = void 0;
|
|
9928
10183
|
const manifest = verifySnapshot(snapshot);
|
|
@@ -9931,7 +10186,7 @@ var exportCmd = new Command17("export").description("Write a portable SQL-text s
|
|
|
9931
10186
|
process.stdout.write(snapshot);
|
|
9932
10187
|
return;
|
|
9933
10188
|
}
|
|
9934
|
-
const out = opts.out ??
|
|
10189
|
+
const out = opts.out ?? join19(personalDir, "snapshots", defaultOutName());
|
|
9935
10190
|
const dir = dirname7(out);
|
|
9936
10191
|
if (dir && dir !== "." && !existsSync18(dir)) {
|
|
9937
10192
|
mkdirSync11(dir, { recursive: true });
|
|
@@ -10318,7 +10573,7 @@ tokenCommand.command("status").description("Show full status of a token for a us
|
|
|
10318
10573
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
10319
10574
|
import { existsSync as existsSync19 } from "fs";
|
|
10320
10575
|
import { homedir as homedir12 } from "os";
|
|
10321
|
-
import { dirname as dirname8, join as
|
|
10576
|
+
import { dirname as dirname8, join as join20 } from "path";
|
|
10322
10577
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10323
10578
|
import { Command as Command20 } from "commander";
|
|
10324
10579
|
var C3 = {
|
|
@@ -10334,8 +10589,8 @@ function findDesktopDir() {
|
|
|
10334
10589
|
for (const start of starts) {
|
|
10335
10590
|
let dir = start;
|
|
10336
10591
|
for (let i = 0; i < 10; i++) {
|
|
10337
|
-
if (existsSync19(
|
|
10338
|
-
return
|
|
10592
|
+
if (existsSync19(join20(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
10593
|
+
return join20(dir, "desktop");
|
|
10339
10594
|
}
|
|
10340
10595
|
const parent = dirname8(dir);
|
|
10341
10596
|
if (parent === dir) break;
|
|
@@ -10345,18 +10600,18 @@ function findDesktopDir() {
|
|
|
10345
10600
|
return null;
|
|
10346
10601
|
}
|
|
10347
10602
|
function findBuiltApp(desktopDir) {
|
|
10348
|
-
const releaseDir =
|
|
10603
|
+
const releaseDir = join20(desktopDir, "src-tauri", "target", "release");
|
|
10349
10604
|
if (process.platform === "win32") {
|
|
10350
10605
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
10351
|
-
const p =
|
|
10606
|
+
const p = join20(releaseDir, name);
|
|
10352
10607
|
if (existsSync19(p)) return p;
|
|
10353
10608
|
}
|
|
10354
10609
|
} else if (process.platform === "darwin") {
|
|
10355
|
-
const app =
|
|
10610
|
+
const app = join20(releaseDir, "bundle", "macos", "ZAM.app");
|
|
10356
10611
|
if (existsSync19(app)) return app;
|
|
10357
10612
|
} else {
|
|
10358
10613
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
10359
|
-
const p =
|
|
10614
|
+
const p = join20(releaseDir, name);
|
|
10360
10615
|
if (existsSync19(p)) return p;
|
|
10361
10616
|
}
|
|
10362
10617
|
}
|
|
@@ -10364,10 +10619,10 @@ function findBuiltApp(desktopDir) {
|
|
|
10364
10619
|
}
|
|
10365
10620
|
function findInstalledApp() {
|
|
10366
10621
|
const candidates = process.platform === "win32" ? [
|
|
10367
|
-
process.env.LOCALAPPDATA &&
|
|
10368
|
-
process.env.ProgramFiles &&
|
|
10369
|
-
process.env["ProgramFiles(x86)"] &&
|
|
10370
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
10622
|
+
process.env.LOCALAPPDATA && join20(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
10623
|
+
process.env.ProgramFiles && join20(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
10624
|
+
process.env["ProgramFiles(x86)"] && join20(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
10625
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join20(homedir12(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
10371
10626
|
return candidates.find((candidate) => candidate && existsSync19(candidate)) || null;
|
|
10372
10627
|
}
|
|
10373
10628
|
function runNpm(args, opts) {
|
|
@@ -10379,7 +10634,7 @@ function runNpm(args, opts) {
|
|
|
10379
10634
|
return res.status ?? 1;
|
|
10380
10635
|
}
|
|
10381
10636
|
function ensureDesktopDeps(desktopDir) {
|
|
10382
|
-
if (existsSync19(
|
|
10637
|
+
if (existsSync19(join20(desktopDir, "node_modules"))) return true;
|
|
10383
10638
|
console.log(
|
|
10384
10639
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
10385
10640
|
);
|
|
@@ -10405,7 +10660,7 @@ function requireRust() {
|
|
|
10405
10660
|
function hasMsvcBuildTools() {
|
|
10406
10661
|
if (process.platform !== "win32") return true;
|
|
10407
10662
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
10408
|
-
const vswhere =
|
|
10663
|
+
const vswhere = join20(
|
|
10409
10664
|
pf86,
|
|
10410
10665
|
"Microsoft Visual Studio",
|
|
10411
10666
|
"Installer",
|
|
@@ -10443,7 +10698,7 @@ function requireMsvcOnWindows() {
|
|
|
10443
10698
|
return false;
|
|
10444
10699
|
}
|
|
10445
10700
|
function warnIfCliMissing(repoRoot) {
|
|
10446
|
-
if (!existsSync19(
|
|
10701
|
+
if (!existsSync19(join20(repoRoot, "dist", "cli", "index.js"))) {
|
|
10447
10702
|
console.warn(
|
|
10448
10703
|
`${C3.yellow}\u26A0 CLI build not found (dist/cli/index.js). The GUI bridge needs it \u2014 run 'npm run build' at the repo root.${C3.reset}`
|
|
10449
10704
|
);
|
|
@@ -10527,7 +10782,7 @@ var uiCommand = new Command20("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
10527
10782
|
);
|
|
10528
10783
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
10529
10784
|
if (code === 0) {
|
|
10530
|
-
const bundle =
|
|
10785
|
+
const bundle = join20(
|
|
10531
10786
|
desktopDir,
|
|
10532
10787
|
"src-tauri",
|
|
10533
10788
|
"target",
|
|
@@ -10603,9 +10858,11 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
10603
10858
|
});
|
|
10604
10859
|
|
|
10605
10860
|
// src/cli/commands/update.ts
|
|
10606
|
-
import {
|
|
10607
|
-
import {
|
|
10861
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
10862
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13, realpathSync } from "fs";
|
|
10863
|
+
import { dirname as dirname9, join as join21 } from "path";
|
|
10608
10864
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
10865
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
10609
10866
|
import { Command as Command21 } from "commander";
|
|
10610
10867
|
var GITHUB_REPO = "zam-os/zam";
|
|
10611
10868
|
var CHANNELS = [
|
|
@@ -10620,6 +10877,7 @@ var C4 = {
|
|
|
10620
10877
|
cyan: "\x1B[36m",
|
|
10621
10878
|
dim: "\x1B[2m",
|
|
10622
10879
|
green: "\x1B[32m",
|
|
10880
|
+
red: "\x1B[31m",
|
|
10623
10881
|
yellow: "\x1B[33m"
|
|
10624
10882
|
};
|
|
10625
10883
|
function currentVersion() {
|
|
@@ -10627,7 +10885,7 @@ function currentVersion() {
|
|
|
10627
10885
|
for (const up of ["..", "../..", "../../.."]) {
|
|
10628
10886
|
try {
|
|
10629
10887
|
const pkg2 = JSON.parse(
|
|
10630
|
-
readFileSync13(
|
|
10888
|
+
readFileSync13(join21(here, up, "package.json"), "utf-8")
|
|
10631
10889
|
);
|
|
10632
10890
|
if (pkg2.version) return pkg2.version;
|
|
10633
10891
|
} catch {
|
|
@@ -10635,6 +10893,16 @@ function currentVersion() {
|
|
|
10635
10893
|
}
|
|
10636
10894
|
return "0.0.0";
|
|
10637
10895
|
}
|
|
10896
|
+
function versionAt(dir) {
|
|
10897
|
+
try {
|
|
10898
|
+
const pkg2 = JSON.parse(
|
|
10899
|
+
readFileSync13(join21(dir, "package.json"), "utf-8")
|
|
10900
|
+
);
|
|
10901
|
+
return pkg2.version ?? "unknown";
|
|
10902
|
+
} catch {
|
|
10903
|
+
return "unknown";
|
|
10904
|
+
}
|
|
10905
|
+
}
|
|
10638
10906
|
async function fetchLatestVersion(repo) {
|
|
10639
10907
|
const res = await fetch(
|
|
10640
10908
|
`https://api.github.com/repos/${repo}/releases/latest`,
|
|
@@ -10704,7 +10972,150 @@ var checkCmd = new Command21("check").description("Check whether a newer ZAM has
|
|
|
10704
10972
|
}
|
|
10705
10973
|
}
|
|
10706
10974
|
);
|
|
10707
|
-
|
|
10975
|
+
function findSourceRepo() {
|
|
10976
|
+
let dir = realpathSync(dirname9(fileURLToPath4(import.meta.url)));
|
|
10977
|
+
let parent = dirname9(dir);
|
|
10978
|
+
while (parent !== dir) {
|
|
10979
|
+
if (existsSync20(join21(dir, ".git"))) return dir;
|
|
10980
|
+
dir = parent;
|
|
10981
|
+
parent = dirname9(dir);
|
|
10982
|
+
}
|
|
10983
|
+
return existsSync20(join21(dir, ".git")) ? dir : null;
|
|
10984
|
+
}
|
|
10985
|
+
function runGit(cwd, args, capture) {
|
|
10986
|
+
const res = spawnSync2("git", args, {
|
|
10987
|
+
cwd,
|
|
10988
|
+
stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
|
|
10989
|
+
encoding: "utf8"
|
|
10990
|
+
});
|
|
10991
|
+
return { ok: res.status === 0, out: (res.stdout ?? "").trim() };
|
|
10992
|
+
}
|
|
10993
|
+
function runNpm2(args, cwd) {
|
|
10994
|
+
const res = spawnSync2("npm", args, {
|
|
10995
|
+
cwd,
|
|
10996
|
+
stdio: "inherit",
|
|
10997
|
+
shell: process.platform === "win32"
|
|
10998
|
+
});
|
|
10999
|
+
return res.status ?? 1;
|
|
11000
|
+
}
|
|
11001
|
+
function runShell(command) {
|
|
11002
|
+
const res = spawnSync2(command, { stdio: "inherit", shell: true });
|
|
11003
|
+
return res.status === 0;
|
|
11004
|
+
}
|
|
11005
|
+
function applyDeveloperUpdate(force) {
|
|
11006
|
+
if (!hasCommand("git")) {
|
|
11007
|
+
console.error(`${C4.red}\u2717${C4.reset} git was not found on PATH.`);
|
|
11008
|
+
process.exit(1);
|
|
11009
|
+
}
|
|
11010
|
+
const src = findSourceRepo();
|
|
11011
|
+
if (!src) {
|
|
11012
|
+
console.error(
|
|
11013
|
+
`${C4.red}\u2717${C4.reset} Could not locate the ZAM source checkout to update.`
|
|
11014
|
+
);
|
|
11015
|
+
process.exit(1);
|
|
11016
|
+
}
|
|
11017
|
+
const status = runGit(src, ["status", "--porcelain"], true);
|
|
11018
|
+
if (status.ok && status.out && !force) {
|
|
11019
|
+
console.error(
|
|
11020
|
+
`${C4.red}\u2717${C4.reset} The source checkout has uncommitted changes:
|
|
11021
|
+
${status.out}
|
|
11022
|
+
Commit or stash them, or re-run with ${C4.cyan}--force${C4.reset}.`
|
|
11023
|
+
);
|
|
11024
|
+
process.exit(1);
|
|
11025
|
+
}
|
|
11026
|
+
console.log(
|
|
11027
|
+
`${C4.dim}\u2192 git pull --ff-only${C4.reset} ${C4.dim}(${src})${C4.reset}`
|
|
11028
|
+
);
|
|
11029
|
+
if (!runGit(src, ["pull", "--ff-only"], false).ok) {
|
|
11030
|
+
console.error(
|
|
11031
|
+
`${C4.red}\u2717${C4.reset} git pull failed \u2014 the branch may have diverged or you are offline. Resolve it manually, then retry.`
|
|
11032
|
+
);
|
|
11033
|
+
process.exit(1);
|
|
11034
|
+
}
|
|
11035
|
+
console.log(`${C4.dim}\u2192 npm install${C4.reset}`);
|
|
11036
|
+
if (runNpm2(["install"], src) !== 0) {
|
|
11037
|
+
console.error(`${C4.red}\u2717${C4.reset} npm install failed.`);
|
|
11038
|
+
process.exit(1);
|
|
11039
|
+
}
|
|
11040
|
+
console.log(`${C4.dim}\u2192 npm run build${C4.reset}`);
|
|
11041
|
+
if (runNpm2(["run", "build"], src) !== 0) {
|
|
11042
|
+
console.error(`${C4.red}\u2717${C4.reset} Build failed.`);
|
|
11043
|
+
process.exit(1);
|
|
11044
|
+
}
|
|
11045
|
+
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
11046
|
+
const setup = spawnSync2(
|
|
11047
|
+
process.execPath,
|
|
11048
|
+
[join21(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
11049
|
+
{ cwd: process.cwd(), stdio: "inherit" }
|
|
11050
|
+
);
|
|
11051
|
+
if (setup.status !== 0) {
|
|
11052
|
+
console.warn(
|
|
11053
|
+
`${C4.yellow}\u26A0${C4.reset} Skill refresh reported a problem \u2014 run '${C4.cyan}zam setup --force${C4.reset}' here manually.`
|
|
11054
|
+
);
|
|
11055
|
+
}
|
|
11056
|
+
console.log(
|
|
11057
|
+
`
|
|
11058
|
+
${C4.green}\u2713${C4.reset} Updated to ${C4.cyan}${versionAt(src)}${C4.reset}. Restart your agent client (e.g. Claude Code) to load the refreshed ${C4.cyan}/zam${C4.reset} skill.`
|
|
11059
|
+
);
|
|
11060
|
+
}
|
|
11061
|
+
async function applyUpdate(opts) {
|
|
11062
|
+
try {
|
|
11063
|
+
const current = currentVersion();
|
|
11064
|
+
const latest = await fetchLatestVersion(GITHUB_REPO);
|
|
11065
|
+
const channel = getInstallChannel();
|
|
11066
|
+
const decision = decideUpdate({
|
|
11067
|
+
currentVersion: current,
|
|
11068
|
+
latestVersion: latest,
|
|
11069
|
+
channel
|
|
11070
|
+
});
|
|
11071
|
+
if (!decision.updateAvailable) {
|
|
11072
|
+
console.log(
|
|
11073
|
+
`${C4.green}\u2713${C4.reset} ZAM is up to date (${C4.cyan}${current}${C4.reset}).`
|
|
11074
|
+
);
|
|
11075
|
+
return;
|
|
11076
|
+
}
|
|
11077
|
+
console.log(
|
|
11078
|
+
`${C4.yellow}\u2191${C4.reset} ${C4.bold}Update available${C4.reset}: ${C4.dim}${current}${C4.reset} \u2192 ${C4.cyan}${latest}${C4.reset} ${C4.dim}(${channel} install)${C4.reset}`
|
|
11079
|
+
);
|
|
11080
|
+
for (const step of planUpdate(decision)) {
|
|
11081
|
+
console.log(` ${C4.dim}\u2022${C4.reset} ${step.label}`);
|
|
11082
|
+
}
|
|
11083
|
+
if (channel === "direct") {
|
|
11084
|
+
console.log(
|
|
11085
|
+
`
|
|
11086
|
+
${C4.dim}This install updates through the desktop app's signed updater \u2014 open ZAM Desktop to install ${latest}.${C4.reset}`
|
|
11087
|
+
);
|
|
11088
|
+
return;
|
|
11089
|
+
}
|
|
11090
|
+
if (!opts.yes) {
|
|
11091
|
+
const ok = await confirm3({
|
|
11092
|
+
message: "Apply this update?",
|
|
11093
|
+
default: true
|
|
11094
|
+
});
|
|
11095
|
+
if (!ok) {
|
|
11096
|
+
console.log("Aborted.");
|
|
11097
|
+
return;
|
|
11098
|
+
}
|
|
11099
|
+
}
|
|
11100
|
+
console.log();
|
|
11101
|
+
if (channel === "winget" || channel === "homebrew") {
|
|
11102
|
+
if (!decision.command || !runShell(decision.command)) process.exit(1);
|
|
11103
|
+
return;
|
|
11104
|
+
}
|
|
11105
|
+
applyDeveloperUpdate(opts.force ?? false);
|
|
11106
|
+
} catch (err) {
|
|
11107
|
+
console.error("Error:", err.message);
|
|
11108
|
+
process.exit(1);
|
|
11109
|
+
}
|
|
11110
|
+
}
|
|
11111
|
+
var updateCommand = new Command21("update").description(
|
|
11112
|
+
"Update ZAM to the latest release (use `update check` to only check)"
|
|
11113
|
+
).option("-y, --yes", "Apply without confirmation").option(
|
|
11114
|
+
"--force",
|
|
11115
|
+
"Update even if the source checkout has uncommitted changes"
|
|
11116
|
+
).action(async (opts) => {
|
|
11117
|
+
await applyUpdate(opts);
|
|
11118
|
+
}).addCommand(checkCmd);
|
|
10708
11119
|
|
|
10709
11120
|
// src/cli/commands/whoami.ts
|
|
10710
11121
|
import { Command as Command22 } from "commander";
|
|
@@ -10745,11 +11156,11 @@ var whoamiCommand = new Command22("whoami").description("Show or set the default
|
|
|
10745
11156
|
|
|
10746
11157
|
// src/cli/commands/workspace.ts
|
|
10747
11158
|
import { execSync as execSync6 } from "child_process";
|
|
10748
|
-
import { existsSync as
|
|
10749
|
-
import { join as
|
|
10750
|
-
import { confirm as
|
|
11159
|
+
import { existsSync as existsSync21, writeFileSync as writeFileSync11 } from "fs";
|
|
11160
|
+
import { join as join22 } from "path";
|
|
11161
|
+
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
10751
11162
|
import { Command as Command23 } from "commander";
|
|
10752
|
-
function
|
|
11163
|
+
function runGit2(cwd, args) {
|
|
10753
11164
|
try {
|
|
10754
11165
|
return execSync6(`git ${args}`, {
|
|
10755
11166
|
cwd,
|
|
@@ -10779,7 +11190,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
10779
11190
|
);
|
|
10780
11191
|
process.exit(1);
|
|
10781
11192
|
}
|
|
10782
|
-
if (!
|
|
11193
|
+
if (!existsSync21(workspaceDir)) {
|
|
10783
11194
|
console.error(
|
|
10784
11195
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
10785
11196
|
);
|
|
@@ -10793,20 +11204,20 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
10793
11204
|
);
|
|
10794
11205
|
process.exit(1);
|
|
10795
11206
|
}
|
|
10796
|
-
const gitignorePath =
|
|
10797
|
-
if (!
|
|
11207
|
+
const gitignorePath = join22(workspaceDir, ".gitignore");
|
|
11208
|
+
if (!existsSync21(gitignorePath)) {
|
|
10798
11209
|
writeFileSync11(
|
|
10799
11210
|
gitignorePath,
|
|
10800
11211
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
10801
11212
|
"utf8"
|
|
10802
11213
|
);
|
|
10803
11214
|
}
|
|
10804
|
-
const hasGitRepo =
|
|
11215
|
+
const hasGitRepo = existsSync21(join22(workspaceDir, ".git"));
|
|
10805
11216
|
if (!hasGitRepo) {
|
|
10806
11217
|
console.log("Initializing local Git repository...");
|
|
10807
|
-
|
|
10808
|
-
|
|
10809
|
-
|
|
11218
|
+
runGit2(workspaceDir, "init -b main");
|
|
11219
|
+
runGit2(workspaceDir, "add .");
|
|
11220
|
+
runGit2(
|
|
10810
11221
|
workspaceDir,
|
|
10811
11222
|
'commit -m "chore: initial workspace sandbox bootstrap"'
|
|
10812
11223
|
);
|
|
@@ -10818,14 +11229,14 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
10818
11229
|
message: "Choose a name for your GitHub repository:",
|
|
10819
11230
|
default: "zam-personal"
|
|
10820
11231
|
});
|
|
10821
|
-
const isPrivate = await
|
|
11232
|
+
const isPrivate = await confirm4({
|
|
10822
11233
|
message: "Should the repository be private?",
|
|
10823
11234
|
default: true
|
|
10824
11235
|
});
|
|
10825
11236
|
const repoVisibility = isPrivate ? "--private" : "--public";
|
|
10826
11237
|
if (hasCommand("gh")) {
|
|
10827
11238
|
console.log("GitHub CLI detected! Automating repository creation...");
|
|
10828
|
-
const proceedGh = await
|
|
11239
|
+
const proceedGh = await confirm4({
|
|
10829
11240
|
message: "Would you like ZAM to create the repository using the GitHub CLI?",
|
|
10830
11241
|
default: true
|
|
10831
11242
|
});
|
|
@@ -10870,16 +11281,16 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
10870
11281
|
console.log("Linking remote repository and pushing...");
|
|
10871
11282
|
let hasOrigin = false;
|
|
10872
11283
|
try {
|
|
10873
|
-
|
|
11284
|
+
runGit2(workspaceDir, "remote get-url origin");
|
|
10874
11285
|
hasOrigin = true;
|
|
10875
11286
|
} catch {
|
|
10876
11287
|
}
|
|
10877
11288
|
if (hasOrigin) {
|
|
10878
|
-
|
|
11289
|
+
runGit2(workspaceDir, `remote set-url origin ${githubUrl}`);
|
|
10879
11290
|
} else {
|
|
10880
|
-
|
|
11291
|
+
runGit2(workspaceDir, `remote add origin ${githubUrl}`);
|
|
10881
11292
|
}
|
|
10882
|
-
|
|
11293
|
+
runGit2(workspaceDir, "push -u origin main");
|
|
10883
11294
|
console.log(
|
|
10884
11295
|
"\x1B[32m\u2713 Successfully linked and pushed to GitHub!\x1B[0m"
|
|
10885
11296
|
);
|
|
@@ -10897,7 +11308,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
10897
11308
|
// src/cli/index.ts
|
|
10898
11309
|
var __dirname = dirname10(fileURLToPath5(import.meta.url));
|
|
10899
11310
|
var pkg = JSON.parse(
|
|
10900
|
-
readFileSync14(
|
|
11311
|
+
readFileSync14(join23(__dirname, "..", "..", "package.json"), "utf-8")
|
|
10901
11312
|
);
|
|
10902
11313
|
var program = new Command24();
|
|
10903
11314
|
program.name("zam").description(
|