squads-cli 0.2.0 → 0.2.1
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 +521 -288
- package/dist/auth-YW3UPFSB.js +23 -0
- package/dist/auth-YW3UPFSB.js.map +1 -0
- package/dist/autonomy-PSVZVX7A.js +105 -0
- package/dist/autonomy-PSVZVX7A.js.map +1 -0
- package/dist/chunk-67RO2HKR.js +174 -0
- package/dist/chunk-67RO2HKR.js.map +1 -0
- package/dist/chunk-7OCVIDC7.js +12 -0
- package/dist/chunk-7OCVIDC7.js.map +1 -0
- package/dist/chunk-BODLDQY7.js +452 -0
- package/dist/chunk-BODLDQY7.js.map +1 -0
- package/dist/chunk-EHQJHRIW.js +103 -0
- package/dist/chunk-EHQJHRIW.js.map +1 -0
- package/dist/chunk-FFFCFZ6A.js +121 -0
- package/dist/chunk-FFFCFZ6A.js.map +1 -0
- package/dist/chunk-FIWT2NMM.js +165 -0
- package/dist/chunk-FIWT2NMM.js.map +1 -0
- package/dist/chunk-HF4WR7RA.js +154 -0
- package/dist/chunk-HF4WR7RA.js.map +1 -0
- package/dist/chunk-J6QF4ZQX.js +230 -0
- package/dist/chunk-J6QF4ZQX.js.map +1 -0
- package/dist/chunk-LOA3KWYJ.js +294 -0
- package/dist/chunk-LOA3KWYJ.js.map +1 -0
- package/dist/chunk-M5FXNY6Y.js +384 -0
- package/dist/chunk-M5FXNY6Y.js.map +1 -0
- package/dist/chunk-QHNUMM4V.js +87 -0
- package/dist/chunk-QHNUMM4V.js.map +1 -0
- package/dist/chunk-QJ7C7CMB.js +223 -0
- package/dist/chunk-QJ7C7CMB.js.map +1 -0
- package/dist/chunk-RM6BWILN.js +74 -0
- package/dist/chunk-RM6BWILN.js.map +1 -0
- package/dist/chunk-TYFTF53O.js +613 -0
- package/dist/chunk-TYFTF53O.js.map +1 -0
- package/dist/chunk-TZXD6WFN.js +420 -0
- package/dist/chunk-TZXD6WFN.js.map +1 -0
- package/dist/chunk-WVOIY5GW.js +621 -0
- package/dist/chunk-WVOIY5GW.js.map +1 -0
- package/dist/chunk-Z2UKDBNL.js +162 -0
- package/dist/chunk-Z2UKDBNL.js.map +1 -0
- package/dist/chunk-ZTQ7ISUR.js +338 -0
- package/dist/chunk-ZTQ7ISUR.js.map +1 -0
- package/dist/cli.js +2483 -5902
- package/dist/cli.js.map +1 -1
- package/dist/context-GWPF4SEY.js +291 -0
- package/dist/context-GWPF4SEY.js.map +1 -0
- package/dist/context-feed-AJGVAR6H.js +394 -0
- package/dist/context-feed-AJGVAR6H.js.map +1 -0
- package/dist/cost-XBCDJ7XC.js +275 -0
- package/dist/cost-XBCDJ7XC.js.map +1 -0
- package/dist/create-BLFGG6PF.js +286 -0
- package/dist/create-BLFGG6PF.js.map +1 -0
- package/dist/dashboard-LGT2B2BL.js +951 -0
- package/dist/dashboard-LGT2B2BL.js.map +1 -0
- package/dist/dashboard-RMK2BOD2.js +794 -0
- package/dist/dashboard-RMK2BOD2.js.map +1 -0
- package/dist/doctor-XPUIIBHJ.js +374 -0
- package/dist/doctor-XPUIIBHJ.js.map +1 -0
- package/dist/env-config-SQEI3Y7Y.js +21 -0
- package/dist/env-config-SQEI3Y7Y.js.map +1 -0
- package/dist/exec-OUXM7JBF.js +223 -0
- package/dist/exec-OUXM7JBF.js.map +1 -0
- package/dist/feedback-KNAOG5QK.js +229 -0
- package/dist/feedback-KNAOG5QK.js.map +1 -0
- package/dist/github-UQTM5KMS.js +23 -0
- package/dist/github-UQTM5KMS.js.map +1 -0
- package/dist/goal-BVHV5573.js +168 -0
- package/dist/goal-BVHV5573.js.map +1 -0
- package/dist/health-4UXN44PF.js +218 -0
- package/dist/health-4UXN44PF.js.map +1 -0
- package/dist/history-ILH3SWHB.js +232 -0
- package/dist/history-ILH3SWHB.js.map +1 -0
- package/dist/index.d.ts +736 -8
- package/dist/index.js +1312 -6
- package/dist/index.js.map +1 -1
- package/dist/init-XQZ7BOGT.js +812 -0
- package/dist/init-XQZ7BOGT.js.map +1 -0
- package/dist/kpi-RQIU7WGK.js +413 -0
- package/dist/kpi-RQIU7WGK.js.map +1 -0
- package/dist/learn-OIFUVZAS.js +269 -0
- package/dist/learn-OIFUVZAS.js.map +1 -0
- package/dist/login-DXZANWZY.js +155 -0
- package/dist/login-DXZANWZY.js.map +1 -0
- package/dist/memory-T3ACCS7E.js +560 -0
- package/dist/memory-T3ACCS7E.js.map +1 -0
- package/dist/memory-VNF2VFRB.js +23 -0
- package/dist/memory-VNF2VFRB.js.map +1 -0
- package/dist/progress-DAUZMT3N.js +202 -0
- package/dist/progress-DAUZMT3N.js.map +1 -0
- package/dist/providers-3P5D2XL5.js +65 -0
- package/dist/providers-3P5D2XL5.js.map +1 -0
- package/dist/results-UECWGLTB.js +224 -0
- package/dist/results-UECWGLTB.js.map +1 -0
- package/dist/run-I6KAXU6U.js +4049 -0
- package/dist/run-I6KAXU6U.js.map +1 -0
- package/dist/session-HBU6KZOD.js +64 -0
- package/dist/session-HBU6KZOD.js.map +1 -0
- package/dist/sessions-CK25VGPL.js +333 -0
- package/dist/sessions-CK25VGPL.js.map +1 -0
- package/dist/squad-parser-DCG65BJS.js +35 -0
- package/dist/squad-parser-DCG65BJS.js.map +1 -0
- package/dist/stats-G6NAU5BD.js +334 -0
- package/dist/stats-G6NAU5BD.js.map +1 -0
- package/dist/status-AQNLDZVN.js +352 -0
- package/dist/status-AQNLDZVN.js.map +1 -0
- package/dist/sync-ZI3MHA4G.js +836 -0
- package/dist/sync-ZI3MHA4G.js.map +1 -0
- package/dist/templates/core/AGENTS.md.template +51 -0
- package/dist/templates/core/BUSINESS_BRIEF.md.template +29 -0
- package/dist/templates/core/CLAUDE.md.template +48 -0
- package/dist/templates/core/provider.yaml.template +5 -0
- package/dist/templates/first-squad/SQUAD.md.template +23 -0
- package/dist/templates/first-squad/lead.md.template +44 -0
- package/dist/templates/memory/getting-started/state.md.template +19 -0
- package/dist/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/dist/templates/seed/CLAUDE.md.template +119 -0
- package/dist/templates/seed/README.md.template +42 -0
- package/dist/templates/seed/config/SYSTEM.md +52 -0
- package/dist/templates/seed/config/provider.yaml +4 -0
- package/dist/templates/seed/hooks/settings.json.template +31 -0
- package/dist/templates/seed/memory/company/directives.md +37 -0
- package/dist/templates/seed/memory/company/manager/state.md +16 -0
- package/dist/templates/seed/memory/engineering/issue-solver/state.md +12 -0
- package/dist/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
- package/dist/templates/seed/memory/marketing/content-drafter/state.md +12 -0
- package/dist/templates/seed/memory/operations/ops-lead/state.md +12 -0
- package/dist/templates/seed/memory/product/lead/state.md +14 -0
- package/dist/templates/seed/memory/research/lead/state.md +14 -0
- package/dist/templates/seed/skills/gh/SKILL.md +57 -0
- package/dist/templates/seed/skills/squads-cli/SKILL.md +84 -0
- package/dist/templates/seed/squads/company/SQUAD.md +51 -0
- package/dist/templates/seed/squads/company/company-critic.md +49 -0
- package/dist/templates/seed/squads/company/company-eval.md +49 -0
- package/dist/templates/seed/squads/company/event-dispatcher.md +43 -0
- package/dist/templates/seed/squads/company/goal-tracker.md +43 -0
- package/dist/templates/seed/squads/company/manager.md +54 -0
- package/dist/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/dist/templates/seed/squads/engineering/code-reviewer.md +57 -0
- package/dist/templates/seed/squads/engineering/issue-solver.md +58 -0
- package/dist/templates/seed/squads/engineering/test-writer.md +50 -0
- package/dist/templates/seed/squads/intelligence/SQUAD.md +38 -0
- package/dist/templates/seed/squads/intelligence/intel-critic.md +36 -0
- package/dist/templates/seed/squads/intelligence/intel-eval.md +31 -0
- package/dist/templates/seed/squads/intelligence/intel-lead.md +71 -0
- package/dist/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/dist/templates/seed/squads/marketing/content-drafter.md +71 -0
- package/dist/templates/seed/squads/marketing/growth-analyst.md +49 -0
- package/dist/templates/seed/squads/marketing/social-poster.md +44 -0
- package/dist/templates/seed/squads/operations/SQUAD.md +45 -0
- package/dist/templates/seed/squads/operations/finance-tracker.md +47 -0
- package/dist/templates/seed/squads/operations/goal-tracker.md +48 -0
- package/dist/templates/seed/squads/operations/ops-lead.md +58 -0
- package/dist/templates/seed/squads/product/SQUAD.md +41 -0
- package/dist/templates/seed/squads/product/lead.md +56 -0
- package/dist/templates/seed/squads/product/scanner.md +50 -0
- package/dist/templates/seed/squads/product/worker.md +55 -0
- package/dist/templates/seed/squads/research/SQUAD.md +38 -0
- package/dist/templates/seed/squads/research/analyst.md +50 -0
- package/dist/templates/seed/squads/research/lead.md +52 -0
- package/dist/templates/seed/squads/research/synthesizer.md +59 -0
- package/dist/templates/skills/squads-learn/SKILL.md +86 -0
- package/dist/templates/skills/squads-workflow/instruction.md +70 -0
- package/dist/terminal-FBQFQTKZ.js +55 -0
- package/dist/terminal-FBQFQTKZ.js.map +1 -0
- package/dist/update-D7CGIZ3M.js +18 -0
- package/dist/update-D7CGIZ3M.js.map +1 -0
- package/dist/update-STU276HR.js +83 -0
- package/dist/update-STU276HR.js.map +1 -0
- package/package.json +31 -13
- package/templates/core/AGENTS.md.template +51 -0
- package/templates/core/BUSINESS_BRIEF.md.template +29 -0
- package/templates/core/CLAUDE.md.template +48 -0
- package/templates/core/provider.yaml.template +5 -0
- package/templates/first-squad/SQUAD.md.template +23 -0
- package/templates/first-squad/lead.md.template +44 -0
- package/templates/memory/getting-started/state.md.template +19 -0
- package/templates/seed/BUSINESS_BRIEF.md.template +27 -0
- package/templates/seed/CLAUDE.md.template +119 -0
- package/templates/seed/README.md.template +42 -0
- package/templates/seed/config/SYSTEM.md +52 -0
- package/templates/seed/config/provider.yaml +4 -0
- package/templates/seed/hooks/settings.json.template +31 -0
- package/templates/seed/memory/company/directives.md +37 -0
- package/templates/seed/memory/company/manager/state.md +16 -0
- package/templates/seed/memory/engineering/issue-solver/state.md +12 -0
- package/templates/seed/memory/intelligence/intel-lead/state.md +9 -0
- package/templates/seed/memory/marketing/content-drafter/state.md +12 -0
- package/templates/seed/memory/operations/ops-lead/state.md +12 -0
- package/templates/seed/memory/product/lead/state.md +14 -0
- package/templates/seed/memory/research/lead/state.md +14 -0
- package/templates/seed/skills/gh/SKILL.md +57 -0
- package/templates/seed/skills/squads-cli/SKILL.md +84 -0
- package/templates/seed/squads/company/SQUAD.md +51 -0
- package/templates/seed/squads/company/company-critic.md +49 -0
- package/templates/seed/squads/company/company-eval.md +49 -0
- package/templates/seed/squads/company/event-dispatcher.md +43 -0
- package/templates/seed/squads/company/goal-tracker.md +43 -0
- package/templates/seed/squads/company/manager.md +54 -0
- package/templates/seed/squads/engineering/SQUAD.md +48 -0
- package/templates/seed/squads/engineering/code-reviewer.md +57 -0
- package/templates/seed/squads/engineering/issue-solver.md +58 -0
- package/templates/seed/squads/engineering/test-writer.md +50 -0
- package/templates/seed/squads/intelligence/SQUAD.md +38 -0
- package/templates/seed/squads/intelligence/intel-critic.md +36 -0
- package/templates/seed/squads/intelligence/intel-eval.md +31 -0
- package/templates/seed/squads/intelligence/intel-lead.md +71 -0
- package/templates/seed/squads/marketing/SQUAD.md +47 -0
- package/templates/seed/squads/marketing/content-drafter.md +71 -0
- package/templates/seed/squads/marketing/growth-analyst.md +49 -0
- package/templates/seed/squads/marketing/social-poster.md +44 -0
- package/templates/seed/squads/operations/SQUAD.md +45 -0
- package/templates/seed/squads/operations/finance-tracker.md +47 -0
- package/templates/seed/squads/operations/goal-tracker.md +48 -0
- package/templates/seed/squads/operations/ops-lead.md +58 -0
- package/templates/seed/squads/product/SQUAD.md +41 -0
- package/templates/seed/squads/product/lead.md +56 -0
- package/templates/seed/squads/product/scanner.md +50 -0
- package/templates/seed/squads/product/worker.md +55 -0
- package/templates/seed/squads/research/SQUAD.md +38 -0
- package/templates/seed/squads/research/analyst.md +50 -0
- package/templates/seed/squads/research/lead.md +52 -0
- package/templates/seed/squads/research/synthesizer.md +59 -0
- package/templates/skills/squads-learn/SKILL.md +86 -0
- package/templates/skills/squads-workflow/instruction.md +70 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/version.ts
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
var require2 = createRequire(import.meta.url);
|
|
6
|
+
var pkg = require2("../package.json");
|
|
7
|
+
var version = pkg.version;
|
|
8
|
+
|
|
9
|
+
// src/lib/telemetry.ts
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir, platform, release } from "os";
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
var TELEMETRY_DIR = join(homedir(), ".squads-cli");
|
|
15
|
+
var CONFIG_PATH = join(TELEMETRY_DIR, "telemetry.json");
|
|
16
|
+
var EVENTS_PATH = join(TELEMETRY_DIR, "events.json");
|
|
17
|
+
var TELEMETRY_ENDPOINT = process.env.SQUADS_TELEMETRY_ENDPOINT || Buffer.from(
|
|
18
|
+
"aHR0cHM6Ly9zcXVhZHMtdGVsZW1ldHJ5LTk3ODg3MTgxNzYxMC51cy1jZW50cmFsMS5ydW4uYXBwL3Bpbmc=",
|
|
19
|
+
"base64"
|
|
20
|
+
).toString();
|
|
21
|
+
var TELEMETRY_KEY = process.env.SQUADS_TELEMETRY_KEY || "";
|
|
22
|
+
var eventQueue = [];
|
|
23
|
+
var flushScheduled = false;
|
|
24
|
+
var cachedSystemContext = null;
|
|
25
|
+
function detectUserType() {
|
|
26
|
+
if (process.env.SQUADS_AGENT) {
|
|
27
|
+
return "agent";
|
|
28
|
+
}
|
|
29
|
+
if (process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true" || process.env.GITLAB_CI === "true" || process.env.JENKINS_URL || process.env.BUILDKITE === "true" || process.env.CIRCLECI === "true" || process.env.AZURE_PIPELINES === "true" || process.env.TF_BUILD === "True") {
|
|
30
|
+
return "ci";
|
|
31
|
+
}
|
|
32
|
+
return "human";
|
|
33
|
+
}
|
|
34
|
+
function getSystemContext() {
|
|
35
|
+
if (cachedSystemContext) return cachedSystemContext;
|
|
36
|
+
cachedSystemContext = {
|
|
37
|
+
os: platform(),
|
|
38
|
+
// darwin, linux, win32
|
|
39
|
+
osVersion: release(),
|
|
40
|
+
nodeVersion: process.version,
|
|
41
|
+
shell: process.env.SHELL?.split("/").pop() || process.env.ComSpec?.split("\\").pop(),
|
|
42
|
+
terminal: process.env.TERM_PROGRAM || void 0,
|
|
43
|
+
ci: process.env.CI === "true" ? "true" : void 0,
|
|
44
|
+
userType: detectUserType(),
|
|
45
|
+
// Agent context (set by squads run)
|
|
46
|
+
squad: process.env.SQUADS_SQUAD || void 0,
|
|
47
|
+
agent: process.env.SQUADS_AGENT || void 0,
|
|
48
|
+
executionId: process.env.SQUADS_EXECUTION_ID || void 0
|
|
49
|
+
};
|
|
50
|
+
return cachedSystemContext;
|
|
51
|
+
}
|
|
52
|
+
function ensureDir() {
|
|
53
|
+
if (!existsSync(TELEMETRY_DIR)) {
|
|
54
|
+
mkdirSync(TELEMETRY_DIR, { recursive: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function getConfig() {
|
|
58
|
+
ensureDir();
|
|
59
|
+
if (!existsSync(CONFIG_PATH)) {
|
|
60
|
+
const config = {
|
|
61
|
+
enabled: true,
|
|
62
|
+
// Opt-out by default (common for CLIs)
|
|
63
|
+
anonymousId: randomUUID(),
|
|
64
|
+
firstRun: (/* @__PURE__ */ new Date()).toISOString()
|
|
65
|
+
};
|
|
66
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
71
|
+
} catch {
|
|
72
|
+
return { enabled: false, anonymousId: "", firstRun: "" };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isEnabled() {
|
|
76
|
+
if (process.env.SQUADS_TELEMETRY_DISABLED === "1") {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (process.env.DO_NOT_TRACK === "1") {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return getConfig().enabled;
|
|
83
|
+
}
|
|
84
|
+
async function track(event, properties) {
|
|
85
|
+
if (!isEnabled()) return;
|
|
86
|
+
const config = getConfig();
|
|
87
|
+
const telemetryEvent = {
|
|
88
|
+
event,
|
|
89
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90
|
+
properties: {
|
|
91
|
+
...properties,
|
|
92
|
+
...getSystemContext(),
|
|
93
|
+
anonymousId: config.anonymousId,
|
|
94
|
+
cliVersion: version
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
storeEventLocally(telemetryEvent);
|
|
98
|
+
eventQueue.push(telemetryEvent);
|
|
99
|
+
if (TELEMETRY_ENDPOINT && !flushScheduled) {
|
|
100
|
+
flushScheduled = true;
|
|
101
|
+
setImmediate(() => {
|
|
102
|
+
flushEvents().catch(() => {
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function flushEvents() {
|
|
108
|
+
if (!TELEMETRY_ENDPOINT || !TELEMETRY_KEY || eventQueue.length === 0) {
|
|
109
|
+
flushScheduled = false;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const batch = [...eventQueue];
|
|
113
|
+
eventQueue = [];
|
|
114
|
+
flushScheduled = false;
|
|
115
|
+
try {
|
|
116
|
+
await fetch(TELEMETRY_ENDPOINT, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
"X-Squads-Key": TELEMETRY_KEY
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({ events: batch }),
|
|
123
|
+
signal: AbortSignal.timeout(5e3)
|
|
124
|
+
});
|
|
125
|
+
} catch {
|
|
126
|
+
eventQueue = [...batch, ...eventQueue].slice(-100);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function storeEventLocally(event) {
|
|
130
|
+
ensureDir();
|
|
131
|
+
let events = [];
|
|
132
|
+
if (existsSync(EVENTS_PATH)) {
|
|
133
|
+
try {
|
|
134
|
+
events = JSON.parse(readFileSync(EVENTS_PATH, "utf-8"));
|
|
135
|
+
} catch {
|
|
136
|
+
events = [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
events.push(event);
|
|
140
|
+
if (events.length > 1e3) {
|
|
141
|
+
events = events.slice(-1e3);
|
|
142
|
+
}
|
|
143
|
+
writeFileSync(EVENTS_PATH, JSON.stringify(events, null, 2));
|
|
144
|
+
}
|
|
145
|
+
var Events = {
|
|
146
|
+
// Lifecycle
|
|
147
|
+
CLI_INIT: "cli.init",
|
|
148
|
+
CLI_ERROR: "cli.error",
|
|
149
|
+
// Commands
|
|
150
|
+
CLI_RUN: "cli.run",
|
|
151
|
+
CLI_STATUS: "cli.status",
|
|
152
|
+
CLI_DASHBOARD: "cli.dashboard",
|
|
153
|
+
CLI_WORKERS: "cli.workers",
|
|
154
|
+
CLI_TONIGHT: "cli.tonight",
|
|
155
|
+
CLI_CONTEXT: "cli.context",
|
|
156
|
+
CLI_COST: "cli.cost",
|
|
157
|
+
CLI_EXEC: "cli.exec",
|
|
158
|
+
CLI_BASELINE: "cli.baseline",
|
|
159
|
+
// Goals
|
|
160
|
+
CLI_GOAL_SET: "cli.goal.set",
|
|
161
|
+
CLI_GOAL_LIST: "cli.goal.list",
|
|
162
|
+
CLI_GOAL_COMPLETE: "cli.goal.complete",
|
|
163
|
+
CLI_GOAL_PROGRESS: "cli.goal.progress",
|
|
164
|
+
// Memory
|
|
165
|
+
CLI_MEMORY_QUERY: "cli.memory.query",
|
|
166
|
+
CLI_MEMORY_SHOW: "cli.memory.show",
|
|
167
|
+
CLI_MEMORY_UPDATE: "cli.memory.update",
|
|
168
|
+
CLI_MEMORY_LIST: "cli.memory.list",
|
|
169
|
+
CLI_MEMORY_SYNC: "cli.memory.sync",
|
|
170
|
+
// Feedback
|
|
171
|
+
CLI_FEEDBACK_ADD: "cli.feedback.add",
|
|
172
|
+
CLI_FEEDBACK_SHOW: "cli.feedback.show",
|
|
173
|
+
CLI_FEEDBACK_STATS: "cli.feedback.stats",
|
|
174
|
+
// Learnings
|
|
175
|
+
CLI_LEARN: "cli.learn",
|
|
176
|
+
CLI_LEARN_SHOW: "cli.learn.show",
|
|
177
|
+
CLI_LEARN_SEARCH: "cli.learn.search",
|
|
178
|
+
// Auth
|
|
179
|
+
CLI_LOGIN: "cli.login",
|
|
180
|
+
CLI_LOGOUT: "cli.logout",
|
|
181
|
+
// Providers
|
|
182
|
+
CLI_PROVIDERS: "cli.providers",
|
|
183
|
+
// KPIs
|
|
184
|
+
CLI_KPI_SHOW: "cli.kpi.show",
|
|
185
|
+
CLI_KPI_RECORD: "cli.kpi.record",
|
|
186
|
+
CLI_KPI_TREND: "cli.kpi.trend",
|
|
187
|
+
CLI_KPI_INSIGHTS: "cli.kpi.insights",
|
|
188
|
+
CLI_KPI_LIST: "cli.kpi.list",
|
|
189
|
+
// Cycle Sync
|
|
190
|
+
CLI_SYNC_CYCLE: "cli.sync.cycle",
|
|
191
|
+
// Context Condenser
|
|
192
|
+
CONDENSER_COMPRESS: "condenser.compress",
|
|
193
|
+
CONDENSER_DEDUPE: "condenser.dedupe",
|
|
194
|
+
CONDENSER_PRUNE: "condenser.prune",
|
|
195
|
+
CONDENSER_SUMMARIZE: "condenser.summarize"
|
|
196
|
+
};
|
|
197
|
+
var exitHandlerRegistered = false;
|
|
198
|
+
function registerExitHandler() {
|
|
199
|
+
if (exitHandlerRegistered) return;
|
|
200
|
+
exitHandlerRegistered = true;
|
|
201
|
+
process.on("beforeExit", async () => {
|
|
202
|
+
if (eventQueue.length > 0) {
|
|
203
|
+
await flushEvents();
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
const signalHandler = async (_signal) => {
|
|
207
|
+
if (eventQueue.length > 0) {
|
|
208
|
+
await flushEvents();
|
|
209
|
+
}
|
|
210
|
+
process.exit(0);
|
|
211
|
+
};
|
|
212
|
+
process.on("SIGINT", () => signalHandler("SIGINT"));
|
|
213
|
+
process.on("SIGTERM", () => signalHandler("SIGTERM"));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export {
|
|
217
|
+
version,
|
|
218
|
+
track,
|
|
219
|
+
flushEvents,
|
|
220
|
+
Events,
|
|
221
|
+
registerExitHandler
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=chunk-QJ7C7CMB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts","../src/lib/telemetry.ts"],"sourcesContent":["import { createRequire } from 'module';\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json');\nexport const version: string = pkg.version;\n","import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir, platform, release } from 'os';\nimport { randomUUID } from 'crypto';\nimport { version as cliVersion } from '../version.js';\n\ninterface TelemetryEvent {\n event: string;\n timestamp: string;\n properties?: Record<string, string | number | boolean | undefined>;\n}\n\ninterface TelemetryConfig {\n enabled: boolean;\n anonymousId: string;\n firstRun: string;\n}\n\nconst TELEMETRY_DIR = join(homedir(), '.squads-cli');\nconst CONFIG_PATH = join(TELEMETRY_DIR, 'telemetry.json');\nconst EVENTS_PATH = join(TELEMETRY_DIR, 'events.json');\n\n// Telemetry endpoint - locked to Agents Squads infrastructure\n// Users can opt-out but cannot redirect telemetry\nconst TELEMETRY_ENDPOINT = process.env.SQUADS_TELEMETRY_ENDPOINT || Buffer.from(\n 'aHR0cHM6Ly9zcXVhZHMtdGVsZW1ldHJ5LTk3ODg3MTgxNzYxMC51cy1jZW50cmFsMS5ydW4uYXBwL3Bpbmc=',\n 'base64'\n).toString();\n\n// API key for endpoint validation — must be set via environment variable\n// NEVER hardcode API keys in source (see: engineering#51)\nconst TELEMETRY_KEY = process.env.SQUADS_TELEMETRY_KEY || '';\n\n// Event queue for batch flushing\nlet eventQueue: TelemetryEvent[] = [];\nlet flushScheduled = false;\n\n// Cached system context (computed once per session)\nlet cachedSystemContext: Record<string, string | undefined> | null = null;\n\n/**\n * Detect user type for segmentation.\n * - 'agent': Autonomous agent execution (via squads run)\n * - 'ci': CI/CD environment (GitHub Actions, Azure Pipelines, etc)\n * - 'human': Interactive terminal usage\n */\nfunction detectUserType(): 'human' | 'agent' | 'ci' {\n // Agent execution (set by squads run command)\n // SQUADS_AGENT contains the agent name when running via squads run\n if (process.env.SQUADS_AGENT) {\n return 'agent';\n }\n\n // CI/CD environments\n if (\n process.env.CI === 'true' ||\n process.env.GITHUB_ACTIONS === 'true' ||\n process.env.GITLAB_CI === 'true' ||\n process.env.JENKINS_URL ||\n process.env.BUILDKITE === 'true' ||\n process.env.CIRCLECI === 'true' ||\n process.env.AZURE_PIPELINES === 'true' ||\n process.env.TF_BUILD === 'True'\n ) {\n return 'ci';\n }\n\n return 'human';\n}\n\n/**\n * Get minimal system context for error identification.\n * Computed once per session for performance.\n */\nfunction getSystemContext(): Record<string, string | undefined> {\n if (cachedSystemContext) return cachedSystemContext;\n\n cachedSystemContext = {\n os: platform(), // darwin, linux, win32\n osVersion: release(),\n nodeVersion: process.version,\n shell: process.env.SHELL?.split('/').pop() || process.env.ComSpec?.split('\\\\').pop(),\n terminal: process.env.TERM_PROGRAM || undefined,\n ci: process.env.CI === 'true' ? 'true' : undefined,\n userType: detectUserType(),\n // Agent context (set by squads run)\n squad: process.env.SQUADS_SQUAD || undefined,\n agent: process.env.SQUADS_AGENT || undefined,\n executionId: process.env.SQUADS_EXECUTION_ID || undefined,\n };\n\n return cachedSystemContext;\n}\n\nfunction ensureDir(): void {\n if (!existsSync(TELEMETRY_DIR)) {\n mkdirSync(TELEMETRY_DIR, { recursive: true });\n }\n}\n\nfunction getConfig(): TelemetryConfig {\n ensureDir();\n\n if (!existsSync(CONFIG_PATH)) {\n const config: TelemetryConfig = {\n enabled: true, // Opt-out by default (common for CLIs)\n anonymousId: randomUUID(),\n firstRun: new Date().toISOString(),\n };\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n return config;\n }\n\n try {\n return JSON.parse(readFileSync(CONFIG_PATH, 'utf-8'));\n } catch {\n return { enabled: false, anonymousId: '', firstRun: '' };\n }\n}\n\nfunction saveConfig(config: TelemetryConfig): void {\n ensureDir();\n writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));\n}\n\n/**\n * Check if telemetry is enabled.\n * Telemetry is disabled if SQUADS_TELEMETRY_DISABLED=1 or DO_NOT_TRACK=1.\n * @returns true if telemetry collection is enabled\n */\nexport function isEnabled(): boolean {\n // Check environment variable first (allows CI/testing override)\n if (process.env.SQUADS_TELEMETRY_DISABLED === '1') {\n return false;\n }\n if (process.env.DO_NOT_TRACK === '1') {\n return false;\n }\n\n return getConfig().enabled;\n}\n\n/**\n * Enable telemetry collection.\n * Persists the setting to ~/.squads-cli/telemetry.json\n */\nexport function enable(): void {\n const config = getConfig();\n config.enabled = true;\n saveConfig(config);\n}\n\n/**\n * Disable telemetry collection.\n * Persists the setting to ~/.squads-cli/telemetry.json\n */\nexport function disable(): void {\n const config = getConfig();\n config.enabled = false;\n saveConfig(config);\n}\n\n/**\n * Get the anonymous identifier for this CLI installation.\n * Generated once on first run and persisted.\n * @returns UUID string\n */\nexport function getAnonymousId(): string {\n return getConfig().anonymousId;\n}\n\n/**\n * Track a telemetry event with optional properties.\n * Events are batched and flushed asynchronously.\n * @param event - Event name (e.g., 'cli.status', 'cli.error')\n * @param properties - Optional key-value pairs of event metadata\n */\nexport async function track(event: string, properties?: Record<string, string | number | boolean | undefined>): Promise<void> {\n if (!isEnabled()) return;\n\n const config = getConfig();\n\n const telemetryEvent: TelemetryEvent = {\n event,\n timestamp: new Date().toISOString(),\n properties: {\n ...properties,\n ...getSystemContext(),\n anonymousId: config.anonymousId,\n cliVersion,\n },\n };\n\n // Store locally (for debugging/review)\n storeEventLocally(telemetryEvent);\n\n // Queue for batch sending\n eventQueue.push(telemetryEvent);\n\n // Schedule flush if not already scheduled\n if (TELEMETRY_ENDPOINT && !flushScheduled) {\n flushScheduled = true;\n // Flush on next tick to batch events from same command\n setImmediate(() => {\n flushEvents().catch(() => {});\n });\n }\n}\n\n/**\n * Flush queued events to the telemetry endpoint\n */\nexport async function flushEvents(): Promise<void> {\n if (!TELEMETRY_ENDPOINT || !TELEMETRY_KEY || eventQueue.length === 0) {\n flushScheduled = false;\n return;\n }\n\n const batch = [...eventQueue];\n eventQueue = [];\n flushScheduled = false;\n\n try {\n await fetch(TELEMETRY_ENDPOINT, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Squads-Key': TELEMETRY_KEY,\n },\n body: JSON.stringify({ events: batch }),\n signal: AbortSignal.timeout(5000),\n });\n } catch {\n // Restore events on failure (will retry on next track)\n eventQueue = [...batch, ...eventQueue].slice(-100); // Keep max 100\n }\n}\n\n/**\n * Track an error event\n */\nexport async function trackError(\n command: string,\n error: Error,\n context?: Record<string, string | number | boolean>\n): Promise<void> {\n await track(Events.CLI_ERROR, {\n command,\n errorType: error.constructor.name,\n errorMessage: error.message.slice(0, 100), // Truncate for privacy\n ...context,\n });\n}\n\n/**\n * Wrap an async command function with telemetry\n */\nexport function instrumentCommand<T>(\n name: string,\n fn: () => Promise<T>\n): () => Promise<T> {\n return async () => {\n const start = Date.now();\n try {\n const result = await fn();\n await track(`cli.${name}`, {\n durationMs: Date.now() - start,\n success: true,\n });\n return result;\n } catch (error) {\n await trackError(name, error as Error, {\n durationMs: Date.now() - start,\n });\n throw error;\n }\n };\n}\n\nfunction storeEventLocally(event: TelemetryEvent): void {\n ensureDir();\n\n let events: TelemetryEvent[] = [];\n\n if (existsSync(EVENTS_PATH)) {\n try {\n events = JSON.parse(readFileSync(EVENTS_PATH, 'utf-8'));\n } catch {\n events = [];\n }\n }\n\n // Keep last 1000 events\n events.push(event);\n if (events.length > 1000) {\n events = events.slice(-1000);\n }\n\n writeFileSync(EVENTS_PATH, JSON.stringify(events, null, 2));\n}\n\n/**\n * Pre-defined event names for consistency across the CLI.\n * Use these constants instead of string literals.\n */\nexport const Events = {\n // Lifecycle\n CLI_INIT: 'cli.init',\n CLI_ERROR: 'cli.error',\n\n // Commands\n CLI_RUN: 'cli.run',\n CLI_STATUS: 'cli.status',\n CLI_DASHBOARD: 'cli.dashboard',\n CLI_WORKERS: 'cli.workers',\n CLI_TONIGHT: 'cli.tonight',\n CLI_CONTEXT: 'cli.context',\n CLI_COST: 'cli.cost',\n CLI_EXEC: 'cli.exec',\n CLI_BASELINE: 'cli.baseline',\n\n // Goals\n CLI_GOAL_SET: 'cli.goal.set',\n CLI_GOAL_LIST: 'cli.goal.list',\n CLI_GOAL_COMPLETE: 'cli.goal.complete',\n CLI_GOAL_PROGRESS: 'cli.goal.progress',\n\n // Memory\n CLI_MEMORY_QUERY: 'cli.memory.query',\n CLI_MEMORY_SHOW: 'cli.memory.show',\n CLI_MEMORY_UPDATE: 'cli.memory.update',\n CLI_MEMORY_LIST: 'cli.memory.list',\n CLI_MEMORY_SYNC: 'cli.memory.sync',\n\n // Feedback\n CLI_FEEDBACK_ADD: 'cli.feedback.add',\n CLI_FEEDBACK_SHOW: 'cli.feedback.show',\n CLI_FEEDBACK_STATS: 'cli.feedback.stats',\n\n // Learnings\n CLI_LEARN: 'cli.learn',\n CLI_LEARN_SHOW: 'cli.learn.show',\n CLI_LEARN_SEARCH: 'cli.learn.search',\n\n // Auth\n CLI_LOGIN: 'cli.login',\n CLI_LOGOUT: 'cli.logout',\n\n // Providers\n CLI_PROVIDERS: 'cli.providers',\n\n // KPIs\n CLI_KPI_SHOW: 'cli.kpi.show',\n CLI_KPI_RECORD: 'cli.kpi.record',\n CLI_KPI_TREND: 'cli.kpi.trend',\n CLI_KPI_INSIGHTS: 'cli.kpi.insights',\n CLI_KPI_LIST: 'cli.kpi.list',\n\n // Cycle Sync\n CLI_SYNC_CYCLE: 'cli.sync.cycle',\n\n // Context Condenser\n CONDENSER_COMPRESS: 'condenser.compress',\n CONDENSER_DEDUPE: 'condenser.dedupe',\n CONDENSER_PRUNE: 'condenser.prune',\n CONDENSER_SUMMARIZE: 'condenser.summarize',\n} as const;\n\n/**\n * Track command execution time.\n * Call at start of command, returns function to call when command completes.\n * @param command - Command name (without 'cli.' prefix)\n * @returns Callback to invoke when command completes\n * @example\n * const done = trackCommand('status');\n * // ... execute command ...\n * done(); // Records duration\n */\nexport function trackCommand(command: string): () => void {\n const start = Date.now();\n\n return () => {\n const duration = Date.now() - start;\n track(`cli.${command}`, { durationMs: duration });\n };\n}\n\n// Register exit handler to flush remaining events\nlet exitHandlerRegistered = false;\n\n/**\n * Register process exit handlers to flush pending telemetry events.\n * Call once at CLI startup. Handles SIGINT, SIGTERM, and normal exit.\n */\nexport function registerExitHandler(): void {\n if (exitHandlerRegistered) return;\n exitHandlerRegistered = true;\n\n // beforeExit allows async operations (unlike 'exit')\n process.on('beforeExit', async () => {\n if (eventQueue.length > 0) {\n await flushEvents();\n }\n });\n\n // For signals, we need to handle manually\n const signalHandler = async (_signal: string) => {\n if (eventQueue.length > 0) {\n await flushEvents();\n }\n process.exit(0);\n };\n\n process.on('SIGINT', () => signalHandler('SIGINT'));\n process.on('SIGTERM', () => signalHandler('SIGTERM'));\n}\n"],"mappings":";;;AAAA,SAAS,qBAAqB;AAC9B,IAAMA,WAAU,cAAc,YAAY,GAAG;AAC7C,IAAM,MAAMA,SAAQ,iBAAiB;AAC9B,IAAM,UAAkB,IAAI;;;ACHnC,SAAS,YAAY,cAAc,eAAe,iBAAiB;AACnE,SAAS,YAAY;AACrB,SAAS,SAAS,UAAU,eAAe;AAC3C,SAAS,kBAAkB;AAe3B,IAAM,gBAAgB,KAAK,QAAQ,GAAG,aAAa;AACnD,IAAM,cAAc,KAAK,eAAe,gBAAgB;AACxD,IAAM,cAAc,KAAK,eAAe,aAAa;AAIrD,IAAM,qBAAqB,QAAQ,IAAI,6BAA6B,OAAO;AAAA,EACzE;AAAA,EACA;AACF,EAAE,SAAS;AAIX,IAAM,gBAAgB,QAAQ,IAAI,wBAAwB;AAG1D,IAAI,aAA+B,CAAC;AACpC,IAAI,iBAAiB;AAGrB,IAAI,sBAAiE;AAQrE,SAAS,iBAA2C;AAGlD,MAAI,QAAQ,IAAI,cAAc;AAC5B,WAAO;AAAA,EACT;AAGA,MACE,QAAQ,IAAI,OAAO,UACnB,QAAQ,IAAI,mBAAmB,UAC/B,QAAQ,IAAI,cAAc,UAC1B,QAAQ,IAAI,eACZ,QAAQ,IAAI,cAAc,UAC1B,QAAQ,IAAI,aAAa,UACzB,QAAQ,IAAI,oBAAoB,UAChC,QAAQ,IAAI,aAAa,QACzB;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAMA,SAAS,mBAAuD;AAC9D,MAAI,oBAAqB,QAAO;AAEhC,wBAAsB;AAAA,IACpB,IAAI,SAAS;AAAA;AAAA,IACb,WAAW,QAAQ;AAAA,IACnB,aAAa,QAAQ;AAAA,IACrB,OAAO,QAAQ,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI,KAAK,QAAQ,IAAI,SAAS,MAAM,IAAI,EAAE,IAAI;AAAA,IACnF,UAAU,QAAQ,IAAI,gBAAgB;AAAA,IACtC,IAAI,QAAQ,IAAI,OAAO,SAAS,SAAS;AAAA,IACzC,UAAU,eAAe;AAAA;AAAA,IAEzB,OAAO,QAAQ,IAAI,gBAAgB;AAAA,IACnC,OAAO,QAAQ,IAAI,gBAAgB;AAAA,IACnC,aAAa,QAAQ,IAAI,uBAAuB;AAAA,EAClD;AAEA,SAAO;AACT;AAEA,SAAS,YAAkB;AACzB,MAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,cAAU,eAAe,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AACF;AAEA,SAAS,YAA6B;AACpC,YAAU;AAEV,MAAI,CAAC,WAAW,WAAW,GAAG;AAC5B,UAAM,SAA0B;AAAA,MAC9B,SAAS;AAAA;AAAA,MACT,aAAa,WAAW;AAAA,MACxB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC;AACA,kBAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC1D,WAAO;AAAA,EACT;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,aAAa,OAAO,CAAC;AAAA,EACtD,QAAQ;AACN,WAAO,EAAE,SAAS,OAAO,aAAa,IAAI,UAAU,GAAG;AAAA,EACzD;AACF;AAYO,SAAS,YAAqB;AAEnC,MAAI,QAAQ,IAAI,8BAA8B,KAAK;AACjD,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,IAAI,iBAAiB,KAAK;AACpC,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,EAAE;AACrB;AAqCA,eAAsB,MAAM,OAAe,YAAmF;AAC5H,MAAI,CAAC,UAAU,EAAG;AAElB,QAAM,SAAS,UAAU;AAEzB,QAAM,iBAAiC;AAAA,IACrC;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,YAAY;AAAA,MACV,GAAG;AAAA,MACH,GAAG,iBAAiB;AAAA,MACpB,aAAa,OAAO;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAGA,oBAAkB,cAAc;AAGhC,aAAW,KAAK,cAAc;AAG9B,MAAI,sBAAsB,CAAC,gBAAgB;AACzC,qBAAiB;AAEjB,iBAAa,MAAM;AACjB,kBAAY,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;AAKA,eAAsB,cAA6B;AACjD,MAAI,CAAC,sBAAsB,CAAC,iBAAiB,WAAW,WAAW,GAAG;AACpE,qBAAiB;AACjB;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,GAAG,UAAU;AAC5B,eAAa,CAAC;AACd,mBAAiB;AAEjB,MAAI;AACF,UAAM,MAAM,oBAAoB;AAAA,MAC9B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,MACtC,QAAQ,YAAY,QAAQ,GAAI;AAAA,IAClC,CAAC;AAAA,EACH,QAAQ;AAEN,iBAAa,CAAC,GAAG,OAAO,GAAG,UAAU,EAAE,MAAM,IAAI;AAAA,EACnD;AACF;AA2CA,SAAS,kBAAkB,OAA6B;AACtD,YAAU;AAEV,MAAI,SAA2B,CAAC;AAEhC,MAAI,WAAW,WAAW,GAAG;AAC3B,QAAI;AACF,eAAS,KAAK,MAAM,aAAa,aAAa,OAAO,CAAC;AAAA,IACxD,QAAQ;AACN,eAAS,CAAC;AAAA,IACZ;AAAA,EACF;AAGA,SAAO,KAAK,KAAK;AACjB,MAAI,OAAO,SAAS,KAAM;AACxB,aAAS,OAAO,MAAM,IAAK;AAAA,EAC7B;AAEA,gBAAc,aAAa,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAC5D;AAMO,IAAM,SAAS;AAAA;AAAA,EAEpB,UAAU;AAAA,EACV,WAAW;AAAA;AAAA,EAGX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc;AAAA;AAAA,EAGd,cAAc;AAAA,EACd,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,mBAAmB;AAAA;AAAA,EAGnB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA;AAAA,EAGjB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,oBAAoB;AAAA;AAAA,EAGpB,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB;AAAA;AAAA,EAGlB,WAAW;AAAA,EACX,YAAY;AAAA;AAAA,EAGZ,eAAe;AAAA;AAAA,EAGf,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,cAAc;AAAA;AAAA,EAGd,gBAAgB;AAAA;AAAA,EAGhB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,qBAAqB;AACvB;AAsBA,IAAI,wBAAwB;AAMrB,SAAS,sBAA4B;AAC1C,MAAI,sBAAuB;AAC3B,0BAAwB;AAGxB,UAAQ,GAAG,cAAc,YAAY;AACnC,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,YAAY;AAAA,IACpB;AAAA,EACF,CAAC;AAGD,QAAM,gBAAgB,OAAO,YAAoB;AAC/C,QAAI,WAAW,SAAS,GAAG;AACzB,YAAM,YAAY;AAAA,IACpB;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,MAAM,cAAc,QAAQ,CAAC;AAClD,UAAQ,GAAG,WAAW,MAAM,cAAc,SAAS,CAAC;AACtD;","names":["require"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/lib/cron.ts
|
|
4
|
+
function cronMatches(cron, date) {
|
|
5
|
+
const parts = cron.trim().split(/\s+/);
|
|
6
|
+
if (parts.length < 5) return false;
|
|
7
|
+
const fields = [
|
|
8
|
+
{ value: date.getMinutes(), field: parts[0], min: 0, max: 59 },
|
|
9
|
+
{ value: date.getHours(), field: parts[1], min: 0, max: 23 },
|
|
10
|
+
{ value: date.getDate(), field: parts[2], min: 1, max: 31 },
|
|
11
|
+
{ value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },
|
|
12
|
+
{ value: date.getDay(), field: parts[4], min: 0, max: 6 }
|
|
13
|
+
];
|
|
14
|
+
return fields.every(
|
|
15
|
+
({ value, field, min, max }) => fieldMatches(field, value, min, max)
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
function fieldMatches(field, value, min, max) {
|
|
19
|
+
if (field.includes(",")) {
|
|
20
|
+
return field.split(",").some((part) => fieldMatches(part.trim(), value, min, max));
|
|
21
|
+
}
|
|
22
|
+
if (field.includes("/")) {
|
|
23
|
+
const [range, stepStr] = field.split("/");
|
|
24
|
+
const step = parseInt(stepStr);
|
|
25
|
+
if (isNaN(step) || step <= 0) return false;
|
|
26
|
+
let start = min;
|
|
27
|
+
let end = max;
|
|
28
|
+
if (range !== "*") {
|
|
29
|
+
if (range.includes("-")) {
|
|
30
|
+
[start, end] = range.split("-").map(Number);
|
|
31
|
+
} else {
|
|
32
|
+
start = parseInt(range);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (value < start || value > end) return false;
|
|
36
|
+
return (value - start) % step === 0;
|
|
37
|
+
}
|
|
38
|
+
if (field.includes("-")) {
|
|
39
|
+
const [start, end] = field.split("-").map(Number);
|
|
40
|
+
return value >= start && value <= end;
|
|
41
|
+
}
|
|
42
|
+
if (field === "*") return true;
|
|
43
|
+
return parseInt(field) === value;
|
|
44
|
+
}
|
|
45
|
+
function getNextCronRun(cron, after = /* @__PURE__ */ new Date()) {
|
|
46
|
+
const next = new Date(after);
|
|
47
|
+
next.setSeconds(0, 0);
|
|
48
|
+
next.setMinutes(next.getMinutes() + 1);
|
|
49
|
+
const maxIterations = 60 * 24 * 8;
|
|
50
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
51
|
+
if (cronMatches(cron, next)) return next;
|
|
52
|
+
next.setMinutes(next.getMinutes() + 1);
|
|
53
|
+
}
|
|
54
|
+
const fallback = new Date(after);
|
|
55
|
+
fallback.setDate(fallback.getDate() + 1);
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
function parseCooldown(cooldown) {
|
|
59
|
+
const match = cooldown.match(/^(\d+)\s*(m|min|minutes?|h|hours?|d|days?)$/i);
|
|
60
|
+
if (!match) return 0;
|
|
61
|
+
const value = parseInt(match[1]);
|
|
62
|
+
const unit = match[2].toLowerCase();
|
|
63
|
+
if (unit.startsWith("m")) return value * 60 * 1e3;
|
|
64
|
+
if (unit.startsWith("h")) return value * 60 * 60 * 1e3;
|
|
65
|
+
if (unit.startsWith("d")) return value * 24 * 60 * 60 * 1e3;
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
cronMatches,
|
|
71
|
+
getNextCronRun,
|
|
72
|
+
parseCooldown
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=chunk-RM6BWILN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/cron.ts"],"sourcesContent":["/**\n * Zero-dependency cron evaluator utilities\n * Extracted from autonomous.ts for reusability and testing\n */\n\n/**\n * Check if a cron expression matches a given date\n * @param cron - Cron expression (minute hour day month weekday)\n * @param date - Date to check against\n * @returns true if the cron matches the date\n */\nexport function cronMatches(cron: string, date: Date): boolean {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length < 5) return false;\n\n const fields = [\n { value: date.getMinutes(), field: parts[0], min: 0, max: 59 },\n { value: date.getHours(), field: parts[1], min: 0, max: 23 },\n { value: date.getDate(), field: parts[2], min: 1, max: 31 },\n { value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },\n { value: date.getDay(), field: parts[4], min: 0, max: 6 },\n ];\n\n return fields.every(({ value, field, min, max }) =>\n fieldMatches(field, value, min, max)\n );\n}\n\n/**\n * Check if a cron field matches a value\n * Handles wildcards, ranges, steps, and lists\n */\nexport function fieldMatches(\n field: string,\n value: number,\n min: number,\n max: number\n): boolean {\n // Handle lists: \"1,3,5\"\n if (field.includes(\",\")) {\n return field.split(\",\").some((part) => fieldMatches(part.trim(), value, min, max));\n }\n\n // Handle step: \"*/5\" or \"1-10/2\"\n if (field.includes(\"/\")) {\n const [range, stepStr] = field.split(\"/\");\n const step = parseInt(stepStr);\n if (isNaN(step) || step <= 0) return false;\n\n let start = min;\n let end = max;\n if (range !== \"*\") {\n if (range.includes(\"-\")) {\n [start, end] = range.split(\"-\").map(Number);\n } else {\n start = parseInt(range);\n }\n }\n if (value < start || value > end) return false;\n return (value - start) % step === 0;\n }\n\n // Handle range: \"1-5\"\n if (field.includes(\"-\")) {\n const [start, end] = field.split(\"-\").map(Number);\n return value >= start && value <= end;\n }\n\n // Wildcard\n if (field === \"*\") return true;\n\n // Exact match\n return parseInt(field) === value;\n}\n\n/**\n * Get the next occurrence of a cron expression after `after`.\n * Brute-forces minute by minute (max 48h lookahead).\n */\nexport function getNextCronRun(cron: string, after: Date = new Date()): Date {\n const next = new Date(after);\n next.setSeconds(0, 0);\n next.setMinutes(next.getMinutes() + 1); // Start from next minute\n\n const maxIterations = 60 * 24 * 8; // 8 days (covers weekly schedules)\n for (let i = 0; i < maxIterations; i++) {\n if (cronMatches(cron, next)) return next;\n next.setMinutes(next.getMinutes() + 1);\n }\n\n // Fallback: 24h from now\n const fallback = new Date(after);\n fallback.setDate(fallback.getDate() + 1);\n return fallback;\n}\n\n/**\n * Parse a cooldown string like \"30m\", \"6 hours\", \"7d\" into milliseconds\n * @param cooldown - String like \"30m\", \"6 hours\", \"7d\"\n * @returns milliseconds, or 0 if invalid\n */\nexport function parseCooldown(cooldown: string): number {\n const match = cooldown.match(/^(\\d+)\\s*(m|min|minutes?|h|hours?|d|days?)$/i);\n if (!match) return 0;\n\n const value = parseInt(match[1]);\n const unit = match[2].toLowerCase();\n\n if (unit.startsWith(\"m\")) return value * 60 * 1000;\n if (unit.startsWith(\"h\")) return value * 60 * 60 * 1000;\n if (unit.startsWith(\"d\")) return value * 24 * 60 * 60 * 1000;\n return 0;\n}\n"],"mappings":"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","names":[]}
|