@inteeka/task-cli 0.1.2 → 0.1.3
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/cli.js +147 -45
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1706,12 +1706,61 @@ function autopilotExitCode(code, status) {
|
|
|
1706
1706
|
|
|
1707
1707
|
// src/scan/llm.ts
|
|
1708
1708
|
import { spawn as spawn2 } from "child_process";
|
|
1709
|
+
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
1710
|
+
import { homedir as homedir4 } from "os";
|
|
1711
|
+
import { join as join6 } from "path";
|
|
1712
|
+
var FIX_PROMPT_JSON_SCHEMA = {
|
|
1713
|
+
type: "object",
|
|
1714
|
+
required: ["summary", "suspected_files", "proposed_changes", "confidence"],
|
|
1715
|
+
additionalProperties: false,
|
|
1716
|
+
properties: {
|
|
1717
|
+
summary: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
1718
|
+
suspected_files: {
|
|
1719
|
+
type: "array",
|
|
1720
|
+
maxItems: 20,
|
|
1721
|
+
items: { type: "string", minLength: 1, maxLength: 500 }
|
|
1722
|
+
},
|
|
1723
|
+
proposed_changes: {
|
|
1724
|
+
type: "array",
|
|
1725
|
+
maxItems: 20,
|
|
1726
|
+
items: {
|
|
1727
|
+
type: "object",
|
|
1728
|
+
required: ["file", "intent"],
|
|
1729
|
+
additionalProperties: false,
|
|
1730
|
+
properties: {
|
|
1731
|
+
file: { type: "string", minLength: 1, maxLength: 500 },
|
|
1732
|
+
intent: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
1733
|
+
rationale: { type: "string", minLength: 1, maxLength: 2e3 }
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
},
|
|
1737
|
+
investigation: {
|
|
1738
|
+
type: "object",
|
|
1739
|
+
additionalProperties: false,
|
|
1740
|
+
properties: {
|
|
1741
|
+
route_match: { type: "string", maxLength: 500 },
|
|
1742
|
+
auth_findings: { type: "string", maxLength: 1500 },
|
|
1743
|
+
middleware_findings: { type: "string", maxLength: 1500 },
|
|
1744
|
+
redirect_findings: { type: "string", maxLength: 1500 },
|
|
1745
|
+
ui_ux_findings: { type: "string", maxLength: 1500 },
|
|
1746
|
+
browser_findings: { type: "string", maxLength: 1500 },
|
|
1747
|
+
api_findings: { type: "string", maxLength: 1500 },
|
|
1748
|
+
data_findings: { type: "string", maxLength: 1500 }
|
|
1749
|
+
}
|
|
1750
|
+
},
|
|
1751
|
+
risk_notes: { type: "string", maxLength: 2e3 },
|
|
1752
|
+
confidence: { type: "string", enum: ["low", "medium", "high"] }
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1709
1755
|
var LlmGenerationError = class extends Error {
|
|
1710
|
-
constructor(reason, message) {
|
|
1756
|
+
constructor(reason, message, debugLogPath) {
|
|
1711
1757
|
super(message);
|
|
1712
1758
|
this.reason = reason;
|
|
1759
|
+
if (debugLogPath !== void 0) this.debugLogPath = debugLogPath;
|
|
1713
1760
|
}
|
|
1761
|
+
debugLogPath;
|
|
1714
1762
|
};
|
|
1763
|
+
var DEBUG = process.env["TASK_SCAN_DEBUG"] === "1";
|
|
1715
1764
|
async function generateFixPromptJson(args) {
|
|
1716
1765
|
const claude = args.claudePath ?? "claude";
|
|
1717
1766
|
const userPrompt = [
|
|
@@ -1719,19 +1768,21 @@ async function generateFixPromptJson(args) {
|
|
|
1719
1768
|
"",
|
|
1720
1769
|
args.ticketBlock,
|
|
1721
1770
|
"",
|
|
1722
|
-
|
|
1723
|
-
"Do not include explanatory prose, markdown fences, or code commentary \u2014 just the JSON object."
|
|
1771
|
+
"Return JSON only matching the supplied schema. Do not include explanatory prose, markdown fences, or commentary."
|
|
1724
1772
|
].join("\n");
|
|
1725
1773
|
const cliArgs = [
|
|
1726
1774
|
"--print",
|
|
1727
|
-
"--
|
|
1775
|
+
"--bare",
|
|
1776
|
+
"--output-format",
|
|
1777
|
+
"json",
|
|
1778
|
+
"--tools",
|
|
1728
1779
|
"",
|
|
1729
1780
|
"--system-prompt",
|
|
1730
1781
|
args.systemPrompt,
|
|
1731
1782
|
"--model",
|
|
1732
1783
|
args.modelId,
|
|
1733
|
-
"--
|
|
1734
|
-
|
|
1784
|
+
"--json-schema",
|
|
1785
|
+
JSON.stringify(FIX_PROMPT_JSON_SCHEMA)
|
|
1735
1786
|
];
|
|
1736
1787
|
return new Promise((resolve2, reject) => {
|
|
1737
1788
|
let child;
|
|
@@ -1756,26 +1807,36 @@ async function generateFixPromptJson(args) {
|
|
|
1756
1807
|
child.on("error", (err) => {
|
|
1757
1808
|
reject(new LlmGenerationError("spawn_failed", err.message));
|
|
1758
1809
|
});
|
|
1759
|
-
child.on("close", (code, signal) => {
|
|
1810
|
+
child.on("close", async (code, signal) => {
|
|
1760
1811
|
if (signal === "SIGTERM" || signal === "SIGKILL") {
|
|
1761
1812
|
reject(new LlmGenerationError("aborted", "claude was aborted"));
|
|
1762
1813
|
return;
|
|
1763
1814
|
}
|
|
1764
1815
|
if (code !== 0) {
|
|
1816
|
+
const dump = await maybeDumpDebug(args.ticketId, stdoutBuf, stderrBuf);
|
|
1765
1817
|
reject(
|
|
1766
1818
|
new LlmGenerationError(
|
|
1767
1819
|
"non_zero_exit",
|
|
1768
|
-
`claude exited with code ${code}: ${stderrBuf.trim().slice(0,
|
|
1820
|
+
`claude exited with code ${code}${dump ? ` (raw output saved to ${dump})` : ""}: ${stderrBuf.trim().slice(0, 600)}`,
|
|
1821
|
+
dump ?? void 0
|
|
1769
1822
|
)
|
|
1770
1823
|
);
|
|
1771
1824
|
return;
|
|
1772
1825
|
}
|
|
1773
|
-
const
|
|
1826
|
+
const innerText = extractEnvelopeText(stdoutBuf);
|
|
1827
|
+
const parsed = parseStructuredJson(innerText);
|
|
1774
1828
|
if (!parsed) {
|
|
1775
|
-
|
|
1829
|
+
const dump = await maybeDumpDebug(args.ticketId, stdoutBuf, stderrBuf);
|
|
1830
|
+
reject(
|
|
1831
|
+
new LlmGenerationError(
|
|
1832
|
+
"no_json",
|
|
1833
|
+
`No JSON object in claude output${dump ? ` (raw output saved to ${dump})` : ""}`,
|
|
1834
|
+
dump ?? void 0
|
|
1835
|
+
)
|
|
1836
|
+
);
|
|
1776
1837
|
return;
|
|
1777
1838
|
}
|
|
1778
|
-
const tokens =
|
|
1839
|
+
const tokens = readEnvelopeTokens(stdoutBuf, userPrompt, innerText);
|
|
1779
1840
|
resolve2({
|
|
1780
1841
|
structured: parsed,
|
|
1781
1842
|
rawText: stdoutBuf,
|
|
@@ -1787,6 +1848,46 @@ async function generateFixPromptJson(args) {
|
|
|
1787
1848
|
child.stdin?.end();
|
|
1788
1849
|
});
|
|
1789
1850
|
}
|
|
1851
|
+
function extractEnvelopeText(raw) {
|
|
1852
|
+
const trimmed = raw.trim();
|
|
1853
|
+
if (!trimmed) return raw;
|
|
1854
|
+
try {
|
|
1855
|
+
const env = JSON.parse(trimmed);
|
|
1856
|
+
if (typeof env.result === "string") return env.result;
|
|
1857
|
+
} catch {
|
|
1858
|
+
}
|
|
1859
|
+
return raw;
|
|
1860
|
+
}
|
|
1861
|
+
function readEnvelopeTokens(raw, userPrompt, innerText) {
|
|
1862
|
+
try {
|
|
1863
|
+
const env = JSON.parse(raw.trim());
|
|
1864
|
+
const inTok = env.input_tokens ?? env.usage?.input_tokens;
|
|
1865
|
+
const outTok = env.output_tokens ?? env.usage?.output_tokens;
|
|
1866
|
+
if (typeof inTok === "number" && typeof outTok === "number") {
|
|
1867
|
+
return { input: inTok, output: outTok };
|
|
1868
|
+
}
|
|
1869
|
+
} catch {
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
input: Math.max(1, Math.round(userPrompt.length / 4)),
|
|
1873
|
+
output: Math.max(1, Math.round(innerText.length / 4))
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
async function maybeDumpDebug(ticketId, stdout, stderr) {
|
|
1877
|
+
if (!DEBUG && stdout.length === 0 && stderr.length === 0) return null;
|
|
1878
|
+
try {
|
|
1879
|
+
const dir = join6(homedir4(), ".cache", "task", "scan-debug");
|
|
1880
|
+
await mkdir5(dir, { recursive: true });
|
|
1881
|
+
const path = join6(dir, `${ticketId}-${Date.now()}.log`);
|
|
1882
|
+
await writeFile6(
|
|
1883
|
+
path,
|
|
1884
|
+
["## ticket_id", ticketId, "", "## stdout", stdout, "", "## stderr", stderr].join("\n")
|
|
1885
|
+
);
|
|
1886
|
+
return path;
|
|
1887
|
+
} catch {
|
|
1888
|
+
return null;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1790
1891
|
function parseStructuredJson(raw) {
|
|
1791
1892
|
const trimmed = raw.trim();
|
|
1792
1893
|
if (!trimmed) return null;
|
|
@@ -1840,12 +1941,6 @@ function parseStructuredJson(raw) {
|
|
|
1840
1941
|
}
|
|
1841
1942
|
return null;
|
|
1842
1943
|
}
|
|
1843
|
-
function estimateTokens(input, output) {
|
|
1844
|
-
return {
|
|
1845
|
-
input: Math.max(1, Math.round(input.length / 4)),
|
|
1846
|
-
output: Math.max(1, Math.round(output.length / 4))
|
|
1847
|
-
};
|
|
1848
|
-
}
|
|
1849
1944
|
|
|
1850
1945
|
// src/commands/scan.ts
|
|
1851
1946
|
function registerScan(program2) {
|
|
@@ -1984,11 +2079,12 @@ ${c.bold(`${project.organisation_slug}/${project.project_slug}`)} ${c.dim(`(${pr
|
|
|
1984
2079
|
const spinner = silent ? null : ora2(`#${ticket.sequence_number} ${ticket.title.slice(0, 60)}`).start();
|
|
1985
2080
|
try {
|
|
1986
2081
|
const generated = await safeGenerate(ticket, claudePath);
|
|
1987
|
-
if (!generated) {
|
|
2082
|
+
if (!generated.ok) {
|
|
1988
2083
|
agg.skipped += 1;
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
);
|
|
2084
|
+
const debugSuffix = generated.debugLogPath ? ` \u2014 debug: ${generated.debugLogPath}` : " \u2014 re-run with TASK_SCAN_DEBUG=1 to capture raw output";
|
|
2085
|
+
spinner?.warn(`#${ticket.sequence_number} skipped (${generated.reason})${debugSuffix}`);
|
|
2086
|
+
await api.abort(skillToken, [ticket.ticket_id]).catch(() => void 0);
|
|
2087
|
+
inFlight.delete(ticket.ticket_id);
|
|
1992
2088
|
continue;
|
|
1993
2089
|
}
|
|
1994
2090
|
const result = await api.submit({
|
|
@@ -2047,12 +2143,18 @@ async function safeGenerate(ticket, claudePath) {
|
|
|
2047
2143
|
ticketBlock: ticket.ticket_block,
|
|
2048
2144
|
outputSchemaHint: ticket.output_schema_hint,
|
|
2049
2145
|
modelId: ticket.model_id,
|
|
2146
|
+
ticketId: ticket.ticket_id,
|
|
2050
2147
|
...claudePath ? { claudePath } : {}
|
|
2051
2148
|
});
|
|
2052
|
-
return out;
|
|
2149
|
+
return { ok: true, ...out };
|
|
2053
2150
|
} catch (err) {
|
|
2054
2151
|
if (err instanceof LlmGenerationError) {
|
|
2055
|
-
|
|
2152
|
+
const result = {
|
|
2153
|
+
ok: false,
|
|
2154
|
+
reason: `${err.reason}: ${err.message.slice(0, 200)}`
|
|
2155
|
+
};
|
|
2156
|
+
if (err.debugLogPath) result.debugLogPath = err.debugLogPath;
|
|
2157
|
+
return result;
|
|
2056
2158
|
}
|
|
2057
2159
|
throw err;
|
|
2058
2160
|
}
|
|
@@ -2070,9 +2172,9 @@ import { randomUUID as randomUUID3 } from "crypto";
|
|
|
2070
2172
|
import { platform as platform2 } from "os";
|
|
2071
2173
|
|
|
2072
2174
|
// src/scheduler/launchd.ts
|
|
2073
|
-
import { mkdir as
|
|
2074
|
-
import { homedir as
|
|
2075
|
-
import { join as
|
|
2175
|
+
import { mkdir as mkdir6, readFile as readFile5, writeFile as writeFile7, unlink as unlink3, readdir } from "fs/promises";
|
|
2176
|
+
import { homedir as homedir5 } from "os";
|
|
2177
|
+
import { join as join7 } from "path";
|
|
2076
2178
|
import { execFileSync as execFileSync5, spawn as spawn3 } from "child_process";
|
|
2077
2179
|
|
|
2078
2180
|
// src/scheduler/cron-translate.ts
|
|
@@ -2174,14 +2276,14 @@ function expandField(field, min, max) {
|
|
|
2174
2276
|
}
|
|
2175
2277
|
|
|
2176
2278
|
// src/scheduler/launchd.ts
|
|
2177
|
-
var PLIST_DIR =
|
|
2279
|
+
var PLIST_DIR = join7(homedir5(), "Library", "LaunchAgents");
|
|
2178
2280
|
var LABEL_PREFIX = "com.inteeka.task.cli.";
|
|
2179
2281
|
var SAFE_ID_RE = /^[0-9a-zA-Z._-]+$/;
|
|
2180
2282
|
function plistPath(id) {
|
|
2181
2283
|
if (!SAFE_ID_RE.test(id) || id.includes("..")) {
|
|
2182
2284
|
throw new Error(`Refusing to compute plist path for unsafe id: ${id}`);
|
|
2183
2285
|
}
|
|
2184
|
-
return
|
|
2286
|
+
return join7(PLIST_DIR, `${LABEL_PREFIX}${id}.plist`);
|
|
2185
2287
|
}
|
|
2186
2288
|
function buildPlist(entry) {
|
|
2187
2289
|
const calendars = translateToLaunchd(entry.cron);
|
|
@@ -2217,9 +2319,9 @@ ${fields}
|
|
|
2217
2319
|
` <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>`,
|
|
2218
2320
|
` </dict>`,
|
|
2219
2321
|
` <key>StandardOutPath</key>`,
|
|
2220
|
-
` <string>${escapeXml(
|
|
2322
|
+
` <string>${escapeXml(join7(homedir5(), ".cache", "task", "launchd-stdout.log"))}</string>`,
|
|
2221
2323
|
` <key>StandardErrorPath</key>`,
|
|
2222
|
-
` <string>${escapeXml(
|
|
2324
|
+
` <string>${escapeXml(join7(homedir5(), ".cache", "task", "launchd-stderr.log"))}</string>`,
|
|
2223
2325
|
!entry.enabled ? ` <key>Disabled</key>
|
|
2224
2326
|
<true/>` : "",
|
|
2225
2327
|
"</dict>",
|
|
@@ -2237,9 +2339,9 @@ function bootstrapDomain() {
|
|
|
2237
2339
|
}
|
|
2238
2340
|
var launchdAdapter = {
|
|
2239
2341
|
async upsert(entry) {
|
|
2240
|
-
await
|
|
2342
|
+
await mkdir6(PLIST_DIR, { recursive: true });
|
|
2241
2343
|
const path = plistPath(entry.id);
|
|
2242
|
-
await
|
|
2344
|
+
await writeFile7(path, buildPlist(entry));
|
|
2243
2345
|
try {
|
|
2244
2346
|
execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2245
2347
|
} catch {
|
|
@@ -2268,7 +2370,7 @@ var launchdAdapter = {
|
|
|
2268
2370
|
for (const file of ours) {
|
|
2269
2371
|
const id = file.slice(LABEL_PREFIX.length, -".plist".length);
|
|
2270
2372
|
try {
|
|
2271
|
-
const xml = await readFile5(
|
|
2373
|
+
const xml = await readFile5(join7(PLIST_DIR, file), "utf8");
|
|
2272
2374
|
const cron = xml.match(/<key>StartCalendarInterval<\/key>[\s\S]*?<\/array>/)?.[0] ?? "";
|
|
2273
2375
|
const command = xml.match(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/)?.[1] ?? "";
|
|
2274
2376
|
const disabled = /<key>Disabled<\/key>\s*<true\/>/.test(xml);
|
|
@@ -2314,7 +2416,7 @@ var launchdAdapter = {
|
|
|
2314
2416
|
}
|
|
2315
2417
|
if (enabled) {
|
|
2316
2418
|
xml = xml.replace(/\s*<key>Disabled<\/key>\s*<true\/>/, "");
|
|
2317
|
-
await
|
|
2419
|
+
await writeFile7(path, xml);
|
|
2318
2420
|
try {
|
|
2319
2421
|
execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
2320
2422
|
} catch {
|
|
@@ -2326,7 +2428,7 @@ var launchdAdapter = {
|
|
|
2326
2428
|
"</dict>\n</plist>",
|
|
2327
2429
|
" <key>Disabled</key>\n <true/>\n</dict>\n</plist>"
|
|
2328
2430
|
);
|
|
2329
|
-
await
|
|
2431
|
+
await writeFile7(path, xml);
|
|
2330
2432
|
}
|
|
2331
2433
|
try {
|
|
2332
2434
|
execFileSync5("launchctl", ["bootout", bootstrapDomain(), path], { stdio: "ignore" });
|
|
@@ -2679,10 +2781,10 @@ var unsupportedAdapter = {
|
|
|
2679
2781
|
};
|
|
2680
2782
|
|
|
2681
2783
|
// src/scheduler/registry.ts
|
|
2682
|
-
import { mkdir as
|
|
2683
|
-
import { homedir as
|
|
2684
|
-
import { dirname as dirname4, join as
|
|
2685
|
-
var REGISTRY_PATH =
|
|
2784
|
+
import { mkdir as mkdir7, readFile as readFile6, writeFile as writeFile8 } from "fs/promises";
|
|
2785
|
+
import { homedir as homedir6 } from "os";
|
|
2786
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
2787
|
+
var REGISTRY_PATH = join8(homedir6(), ".config", "task", "schedules.json");
|
|
2686
2788
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2687
2789
|
function looksLikeRegistryRow(value) {
|
|
2688
2790
|
if (!value || typeof value !== "object") return false;
|
|
@@ -2702,8 +2804,8 @@ async function readRegistry() {
|
|
|
2702
2804
|
}
|
|
2703
2805
|
}
|
|
2704
2806
|
async function writeRegistry(rows) {
|
|
2705
|
-
await
|
|
2706
|
-
await
|
|
2807
|
+
await mkdir7(dirname4(REGISTRY_PATH), { recursive: true });
|
|
2808
|
+
await writeFile8(REGISTRY_PATH, JSON.stringify(rows, null, 2));
|
|
2707
2809
|
}
|
|
2708
2810
|
async function upsertRegistry(row) {
|
|
2709
2811
|
if (!UUID_RE.test(row.id)) {
|
|
@@ -2943,8 +3045,8 @@ function stripAnsi(s) {
|
|
|
2943
3045
|
|
|
2944
3046
|
// src/commands/runs.ts
|
|
2945
3047
|
import { readFile as readFile7 } from "fs/promises";
|
|
2946
|
-
import { homedir as
|
|
2947
|
-
import { join as
|
|
3048
|
+
import { homedir as homedir7 } from "os";
|
|
3049
|
+
import { join as join9 } from "path";
|
|
2948
3050
|
function registerRuns(program2) {
|
|
2949
3051
|
const cmd = program2.command("runs").description("Inspect agentic CLI run history");
|
|
2950
3052
|
cmd.command("list").description("List recent runs").option("--limit <n>", "Max rows", "50").option("--ticket <id>", "Filter by ticket").option("--schedule <id>", "Filter by schedule").action(async (opts) => {
|
|
@@ -2973,7 +3075,7 @@ function registerRuns(program2) {
|
|
|
2973
3075
|
process.stdout.write(JSON.stringify(row, null, 2) + "\n");
|
|
2974
3076
|
});
|
|
2975
3077
|
cmd.command("logs <id>").description("Show captured agent output for a run, if available").action(async (id) => {
|
|
2976
|
-
const localPath =
|
|
3078
|
+
const localPath = join9(homedir7(), ".cache", "task", "runs", `${id}.log`);
|
|
2977
3079
|
try {
|
|
2978
3080
|
const text = await readFile7(localPath, "utf8");
|
|
2979
3081
|
process.stdout.write(text);
|
|
@@ -3125,7 +3227,7 @@ function checkBinary(name, command) {
|
|
|
3125
3227
|
}
|
|
3126
3228
|
|
|
3127
3229
|
// src/commands/version.ts
|
|
3128
|
-
var CLI_VERSION = true ? "0.1.
|
|
3230
|
+
var CLI_VERSION = true ? "0.1.3" : "0.0.0-dev";
|
|
3129
3231
|
function registerVersion(program2) {
|
|
3130
3232
|
program2.command("version").description("Print the CLI version").action(() => {
|
|
3131
3233
|
process.stdout.write(CLI_VERSION + "\n");
|