botinabox 0.2.4 → 0.3.0
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/CHANGELOG.md +9 -0
- package/dist/index.js +630 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [S
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.3.0] — 2026-04-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Domain tables** — `defineDomainTables(db, options?)` creates standard multi-agent app tables: org, project, client, invoice, repository, file, channel, rule, event + junction tables. Configurable: disable clients, repos, files, channels, rules, or events.
|
|
14
|
+
- **Domain entity contexts** — `defineDomainEntityContexts(db, options?)` renders per-entity context directories for all domain tables. Projects get REPOS.md + RULES.md. Clients get REPOS.md + AGENTS.md + INVOICES.md.
|
|
15
|
+
- **Claude stream parser** — `parseClaudeStream(stdout)` parses Claude CLI NDJSON output into structured results (session, model, cost, tokens, text, errors). Plus `isMaxTurns()`, `isLoginRequired()`, `deactivateLocalImagePaths()`.
|
|
16
|
+
- **Process env builder** — `buildProcessEnv(allowedKeys?, inject?)` creates a clean subprocess environment with only safe variables. Strips all secrets.
|
|
17
|
+
|
|
9
18
|
## [0.2.0] — 2026-04-03
|
|
10
19
|
|
|
11
20
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -1140,7 +1140,10 @@ var DataStore = class {
|
|
|
1140
1140
|
protectedFiles: def.protectedFiles,
|
|
1141
1141
|
protected: def.protected,
|
|
1142
1142
|
encrypted: def.encrypted,
|
|
1143
|
-
|
|
1143
|
+
// Note: sourceDefaults.softDelete NOT set here because junction tables
|
|
1144
|
+
// (agent_project, rule_agent, etc.) don't have deleted_at columns.
|
|
1145
|
+
// Entity context render functions should add softDelete per-source when targeting
|
|
1146
|
+
// tables that support it.
|
|
1144
1147
|
index: def.indexFile ? {
|
|
1145
1148
|
outputFile: def.indexFile,
|
|
1146
1149
|
render: def.indexRender ?? ((rows) => {
|
|
@@ -1670,6 +1673,501 @@ ${s.definition}` : null,
|
|
|
1670
1673
|
});
|
|
1671
1674
|
}
|
|
1672
1675
|
|
|
1676
|
+
// src/core/data/domain-schema.ts
|
|
1677
|
+
function defineDomainTables(db, options = {}) {
|
|
1678
|
+
const opts = {
|
|
1679
|
+
clients: true,
|
|
1680
|
+
repositories: true,
|
|
1681
|
+
files: true,
|
|
1682
|
+
channels: true,
|
|
1683
|
+
rules: true,
|
|
1684
|
+
events: true,
|
|
1685
|
+
...options
|
|
1686
|
+
};
|
|
1687
|
+
db.define("org", {
|
|
1688
|
+
columns: {
|
|
1689
|
+
id: "TEXT PRIMARY KEY",
|
|
1690
|
+
name: "TEXT NOT NULL",
|
|
1691
|
+
type: "TEXT NOT NULL DEFAULT 'company'",
|
|
1692
|
+
description: "TEXT",
|
|
1693
|
+
mission: "TEXT",
|
|
1694
|
+
website: "TEXT",
|
|
1695
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1696
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1697
|
+
deleted_at: "TEXT"
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
db.define("project", {
|
|
1701
|
+
columns: {
|
|
1702
|
+
id: "TEXT PRIMARY KEY",
|
|
1703
|
+
org_id: "TEXT NOT NULL",
|
|
1704
|
+
name: "TEXT NOT NULL",
|
|
1705
|
+
status: "TEXT",
|
|
1706
|
+
description: "TEXT",
|
|
1707
|
+
tech_stack: "TEXT",
|
|
1708
|
+
github_repo: "TEXT",
|
|
1709
|
+
deploy_target: "TEXT",
|
|
1710
|
+
production_url: "TEXT",
|
|
1711
|
+
branch_strategy: "TEXT",
|
|
1712
|
+
notes: "TEXT",
|
|
1713
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1714
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1715
|
+
deleted_at: "TEXT"
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
db.define("agent_project", {
|
|
1719
|
+
columns: {
|
|
1720
|
+
agent_id: "TEXT NOT NULL",
|
|
1721
|
+
project_id: "TEXT NOT NULL",
|
|
1722
|
+
role: "TEXT",
|
|
1723
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1724
|
+
},
|
|
1725
|
+
primaryKey: ["agent_id", "project_id"]
|
|
1726
|
+
});
|
|
1727
|
+
db.define("secret_project", {
|
|
1728
|
+
columns: {
|
|
1729
|
+
secret_id: "TEXT NOT NULL",
|
|
1730
|
+
project_id: "TEXT NOT NULL",
|
|
1731
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1732
|
+
},
|
|
1733
|
+
primaryKey: ["secret_id", "project_id"]
|
|
1734
|
+
});
|
|
1735
|
+
db.define("secret_org", {
|
|
1736
|
+
columns: {
|
|
1737
|
+
secret_id: "TEXT NOT NULL",
|
|
1738
|
+
org_id: "TEXT NOT NULL",
|
|
1739
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1740
|
+
},
|
|
1741
|
+
primaryKey: ["secret_id", "org_id"]
|
|
1742
|
+
});
|
|
1743
|
+
if (opts.clients) {
|
|
1744
|
+
db.define("client", {
|
|
1745
|
+
columns: {
|
|
1746
|
+
id: "TEXT PRIMARY KEY",
|
|
1747
|
+
org_id: "TEXT NOT NULL",
|
|
1748
|
+
name: "TEXT NOT NULL",
|
|
1749
|
+
contact_name: "TEXT",
|
|
1750
|
+
contact_email: "TEXT",
|
|
1751
|
+
phone: "TEXT",
|
|
1752
|
+
status: "TEXT NOT NULL DEFAULT 'active'",
|
|
1753
|
+
notes: "TEXT",
|
|
1754
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1755
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1756
|
+
deleted_at: "TEXT"
|
|
1757
|
+
}
|
|
1758
|
+
});
|
|
1759
|
+
db.define("invoice", {
|
|
1760
|
+
columns: {
|
|
1761
|
+
id: "TEXT PRIMARY KEY",
|
|
1762
|
+
org_id: "TEXT NOT NULL",
|
|
1763
|
+
client_id: "TEXT NOT NULL",
|
|
1764
|
+
number: "TEXT",
|
|
1765
|
+
amount_cents: "INTEGER",
|
|
1766
|
+
currency: "TEXT DEFAULT 'USD'",
|
|
1767
|
+
status: "TEXT NOT NULL DEFAULT 'draft'",
|
|
1768
|
+
description: "TEXT",
|
|
1769
|
+
hours: "REAL",
|
|
1770
|
+
rate_cents: "INTEGER",
|
|
1771
|
+
items_json: "TEXT",
|
|
1772
|
+
issued_at: "TEXT",
|
|
1773
|
+
due_at: "TEXT",
|
|
1774
|
+
paid_at: "TEXT",
|
|
1775
|
+
notes: "TEXT",
|
|
1776
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1777
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1778
|
+
deleted_at: "TEXT"
|
|
1779
|
+
},
|
|
1780
|
+
tableConstraints: [
|
|
1781
|
+
"CREATE INDEX IF NOT EXISTS idx_invoice_client ON invoice(client_id)"
|
|
1782
|
+
]
|
|
1783
|
+
});
|
|
1784
|
+
db.define("agent_client", {
|
|
1785
|
+
columns: {
|
|
1786
|
+
agent_id: "TEXT NOT NULL",
|
|
1787
|
+
client_id: "TEXT NOT NULL",
|
|
1788
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1789
|
+
},
|
|
1790
|
+
primaryKey: ["agent_id", "client_id"]
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
if (opts.repositories) {
|
|
1794
|
+
db.define("repository", {
|
|
1795
|
+
columns: {
|
|
1796
|
+
id: "TEXT PRIMARY KEY",
|
|
1797
|
+
org_id: "TEXT NOT NULL",
|
|
1798
|
+
project_id: "TEXT",
|
|
1799
|
+
client_id: "TEXT",
|
|
1800
|
+
name: "TEXT NOT NULL",
|
|
1801
|
+
url: "TEXT",
|
|
1802
|
+
local_path: "TEXT",
|
|
1803
|
+
default_branch: "TEXT DEFAULT 'main'",
|
|
1804
|
+
platform: "TEXT DEFAULT 'github'",
|
|
1805
|
+
status: "TEXT NOT NULL DEFAULT 'active'",
|
|
1806
|
+
notes: "TEXT",
|
|
1807
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1808
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1809
|
+
deleted_at: "TEXT"
|
|
1810
|
+
},
|
|
1811
|
+
tableConstraints: [
|
|
1812
|
+
"CREATE INDEX IF NOT EXISTS idx_repository_project ON repository(project_id)",
|
|
1813
|
+
"CREATE INDEX IF NOT EXISTS idx_repository_client ON repository(client_id)"
|
|
1814
|
+
]
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
if (opts.files) {
|
|
1818
|
+
db.define("file", {
|
|
1819
|
+
columns: {
|
|
1820
|
+
id: "TEXT PRIMARY KEY",
|
|
1821
|
+
org_id: "TEXT",
|
|
1822
|
+
name: "TEXT NOT NULL",
|
|
1823
|
+
file_path: "TEXT",
|
|
1824
|
+
mime_type: "TEXT",
|
|
1825
|
+
size_bytes: "INTEGER",
|
|
1826
|
+
project_id: "TEXT",
|
|
1827
|
+
access_level: "TEXT NOT NULL DEFAULT 'org'",
|
|
1828
|
+
description: "TEXT",
|
|
1829
|
+
notes: "TEXT",
|
|
1830
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1831
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1832
|
+
deleted_at: "TEXT"
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
if (opts.channels) {
|
|
1837
|
+
db.define("channel", {
|
|
1838
|
+
columns: {
|
|
1839
|
+
id: "TEXT PRIMARY KEY",
|
|
1840
|
+
org_id: "TEXT",
|
|
1841
|
+
platform: "TEXT NOT NULL",
|
|
1842
|
+
external_id: "TEXT",
|
|
1843
|
+
name: "TEXT NOT NULL",
|
|
1844
|
+
type: "TEXT NOT NULL DEFAULT 'channel'",
|
|
1845
|
+
instructions: "TEXT",
|
|
1846
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1847
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1848
|
+
deleted_at: "TEXT"
|
|
1849
|
+
}
|
|
1850
|
+
});
|
|
1851
|
+
}
|
|
1852
|
+
if (opts.rules) {
|
|
1853
|
+
db.define("rule", {
|
|
1854
|
+
columns: {
|
|
1855
|
+
id: "TEXT PRIMARY KEY",
|
|
1856
|
+
org_id: "TEXT",
|
|
1857
|
+
title: "TEXT NOT NULL",
|
|
1858
|
+
rule_text: "TEXT NOT NULL",
|
|
1859
|
+
scope: "TEXT NOT NULL DEFAULT 'org'",
|
|
1860
|
+
category: "TEXT NOT NULL DEFAULT 'process'",
|
|
1861
|
+
priority: "INTEGER NOT NULL DEFAULT 50",
|
|
1862
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1863
|
+
updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1864
|
+
deleted_at: "TEXT"
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
db.define("rule_agent", {
|
|
1868
|
+
columns: {
|
|
1869
|
+
rule_id: "TEXT NOT NULL",
|
|
1870
|
+
agent_id: "TEXT NOT NULL",
|
|
1871
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1872
|
+
},
|
|
1873
|
+
primaryKey: ["rule_id", "agent_id"]
|
|
1874
|
+
});
|
|
1875
|
+
db.define("rule_project", {
|
|
1876
|
+
columns: {
|
|
1877
|
+
rule_id: "TEXT NOT NULL",
|
|
1878
|
+
project_id: "TEXT NOT NULL",
|
|
1879
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1880
|
+
},
|
|
1881
|
+
primaryKey: ["rule_id", "project_id"]
|
|
1882
|
+
});
|
|
1883
|
+
db.define("rule_org", {
|
|
1884
|
+
columns: {
|
|
1885
|
+
rule_id: "TEXT NOT NULL",
|
|
1886
|
+
org_id: "TEXT NOT NULL",
|
|
1887
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
|
|
1888
|
+
},
|
|
1889
|
+
primaryKey: ["rule_id", "org_id"]
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
if (opts.events) {
|
|
1893
|
+
db.define("event", {
|
|
1894
|
+
columns: {
|
|
1895
|
+
id: "TEXT PRIMARY KEY",
|
|
1896
|
+
org_id: "TEXT",
|
|
1897
|
+
type: "TEXT NOT NULL",
|
|
1898
|
+
summary: "TEXT NOT NULL",
|
|
1899
|
+
details: "TEXT",
|
|
1900
|
+
severity: "TEXT NOT NULL DEFAULT 'info'",
|
|
1901
|
+
actor_agent_id: "TEXT",
|
|
1902
|
+
actor_user_id: "TEXT",
|
|
1903
|
+
project_id: "TEXT",
|
|
1904
|
+
channel_id: "TEXT",
|
|
1905
|
+
created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
|
|
1906
|
+
deleted_at: "TEXT"
|
|
1907
|
+
},
|
|
1908
|
+
tableConstraints: [
|
|
1909
|
+
"CREATE INDEX IF NOT EXISTS idx_event_type ON event(type, created_at)",
|
|
1910
|
+
"CREATE INDEX IF NOT EXISTS idx_event_project ON event(project_id)"
|
|
1911
|
+
]
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// src/core/data/domain-entity-contexts.ts
|
|
1917
|
+
function defineDomainEntityContexts(db, options = {}) {
|
|
1918
|
+
const opts = {
|
|
1919
|
+
clients: true,
|
|
1920
|
+
repositories: true,
|
|
1921
|
+
files: true,
|
|
1922
|
+
channels: true,
|
|
1923
|
+
rules: true,
|
|
1924
|
+
...options
|
|
1925
|
+
};
|
|
1926
|
+
db.defineEntityContext("org", {
|
|
1927
|
+
table: "org",
|
|
1928
|
+
directory: "orgs",
|
|
1929
|
+
slugColumn: "name",
|
|
1930
|
+
indexFile: "orgs/ORGS.md",
|
|
1931
|
+
files: {
|
|
1932
|
+
"ORG.md": {
|
|
1933
|
+
source: { type: "self" },
|
|
1934
|
+
render: (rows) => {
|
|
1935
|
+
const o = rows[0];
|
|
1936
|
+
if (!o) return "";
|
|
1937
|
+
return [
|
|
1938
|
+
`# ${o.name}`,
|
|
1939
|
+
"",
|
|
1940
|
+
o.type ? `**Type:** ${o.type}` : null,
|
|
1941
|
+
o.description ? `
|
|
1942
|
+
${o.description}` : null,
|
|
1943
|
+
o.mission ? `
|
|
1944
|
+
**Mission:** ${o.mission}` : null,
|
|
1945
|
+
o.website ? `**Website:** ${o.website}` : null,
|
|
1946
|
+
""
|
|
1947
|
+
].filter(Boolean).join("\n");
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
db.defineEntityContext("project", {
|
|
1953
|
+
table: "project",
|
|
1954
|
+
directory: "projects",
|
|
1955
|
+
slugColumn: "name",
|
|
1956
|
+
indexFile: "projects/PROJECTS.md",
|
|
1957
|
+
files: {
|
|
1958
|
+
"PROJECT.md": {
|
|
1959
|
+
source: { type: "self" },
|
|
1960
|
+
render: (rows) => {
|
|
1961
|
+
const p = rows[0];
|
|
1962
|
+
if (!p) return "";
|
|
1963
|
+
return [
|
|
1964
|
+
`# ${p.name}`,
|
|
1965
|
+
"",
|
|
1966
|
+
p.status ? `**Status:** ${p.status}` : null,
|
|
1967
|
+
p.description ? `
|
|
1968
|
+
${p.description}` : null,
|
|
1969
|
+
p.tech_stack ? `
|
|
1970
|
+
**Tech Stack:** ${p.tech_stack}` : null,
|
|
1971
|
+
p.production_url ? `**URL:** ${p.production_url}` : null,
|
|
1972
|
+
p.github_repo ? `**GitHub:** ${p.github_repo}` : null,
|
|
1973
|
+
p.deploy_target ? `**Deploy:** ${p.deploy_target}` : null,
|
|
1974
|
+
p.branch_strategy ? `**Branch Strategy:** ${p.branch_strategy}` : null,
|
|
1975
|
+
p.notes ? `
|
|
1976
|
+
**Notes:**
|
|
1977
|
+
${p.notes}` : null,
|
|
1978
|
+
""
|
|
1979
|
+
].filter(Boolean).join("\n");
|
|
1980
|
+
}
|
|
1981
|
+
},
|
|
1982
|
+
...opts.repositories ? {
|
|
1983
|
+
"REPOS.md": {
|
|
1984
|
+
source: {
|
|
1985
|
+
type: "hasMany",
|
|
1986
|
+
table: "repository",
|
|
1987
|
+
foreignKey: "project_id"
|
|
1988
|
+
},
|
|
1989
|
+
render: (rows) => {
|
|
1990
|
+
if (!rows.length) return "";
|
|
1991
|
+
const lines = rows.map(
|
|
1992
|
+
(r) => `- **${r.name}** \u2014 ${r.url ?? ""}`
|
|
1993
|
+
);
|
|
1994
|
+
return `# Repositories
|
|
1995
|
+
|
|
1996
|
+
${lines.join("\n")}
|
|
1997
|
+
`;
|
|
1998
|
+
},
|
|
1999
|
+
omitIfEmpty: true
|
|
2000
|
+
}
|
|
2001
|
+
} : {},
|
|
2002
|
+
...opts.rules ? {
|
|
2003
|
+
"RULES.md": {
|
|
2004
|
+
source: {
|
|
2005
|
+
type: "manyToMany",
|
|
2006
|
+
junctionTable: "rule_project",
|
|
2007
|
+
localKey: "project_id",
|
|
2008
|
+
remoteKey: "rule_id",
|
|
2009
|
+
remoteTable: "rule",
|
|
2010
|
+
softDelete: true,
|
|
2011
|
+
orderBy: "priority"
|
|
2012
|
+
},
|
|
2013
|
+
render: (rows) => {
|
|
2014
|
+
if (!rows.length) return "";
|
|
2015
|
+
const lines = rows.map(
|
|
2016
|
+
(r) => `### ${r.title}
|
|
2017
|
+
${r.rule_text}`
|
|
2018
|
+
);
|
|
2019
|
+
return `# Project Rules
|
|
2020
|
+
|
|
2021
|
+
${lines.join("\n\n")}
|
|
2022
|
+
`;
|
|
2023
|
+
},
|
|
2024
|
+
omitIfEmpty: true
|
|
2025
|
+
}
|
|
2026
|
+
} : {}
|
|
2027
|
+
}
|
|
2028
|
+
});
|
|
2029
|
+
if (opts.clients) {
|
|
2030
|
+
db.defineEntityContext("client", {
|
|
2031
|
+
table: "client",
|
|
2032
|
+
directory: "clients",
|
|
2033
|
+
slugColumn: "name",
|
|
2034
|
+
indexFile: "clients/CLIENTS.md",
|
|
2035
|
+
files: {
|
|
2036
|
+
"CLIENT.md": {
|
|
2037
|
+
source: { type: "self" },
|
|
2038
|
+
render: (rows) => {
|
|
2039
|
+
const c = rows[0];
|
|
2040
|
+
if (!c) return "";
|
|
2041
|
+
return [
|
|
2042
|
+
`# ${c.name}`,
|
|
2043
|
+
"",
|
|
2044
|
+
c.contact_name ? `**Contact:** ${c.contact_name}` : null,
|
|
2045
|
+
c.contact_email ? `**Email:** ${c.contact_email}` : null,
|
|
2046
|
+
c.phone ? `**Phone:** ${c.phone}` : null,
|
|
2047
|
+
c.status ? `**Status:** ${c.status}` : null,
|
|
2048
|
+
c.notes ? `
|
|
2049
|
+
${c.notes}` : null,
|
|
2050
|
+
""
|
|
2051
|
+
].filter(Boolean).join("\n");
|
|
2052
|
+
}
|
|
2053
|
+
},
|
|
2054
|
+
...opts.repositories ? {
|
|
2055
|
+
"REPOS.md": {
|
|
2056
|
+
source: {
|
|
2057
|
+
type: "hasMany",
|
|
2058
|
+
table: "repository",
|
|
2059
|
+
foreignKey: "client_id"
|
|
2060
|
+
},
|
|
2061
|
+
render: (rows) => {
|
|
2062
|
+
if (!rows.length) return "";
|
|
2063
|
+
const lines = rows.map(
|
|
2064
|
+
(r) => `- **${r.name}** \u2014 ${r.url ?? ""}`
|
|
2065
|
+
);
|
|
2066
|
+
return `# Repositories
|
|
2067
|
+
|
|
2068
|
+
${lines.join("\n")}
|
|
2069
|
+
`;
|
|
2070
|
+
},
|
|
2071
|
+
omitIfEmpty: true
|
|
2072
|
+
}
|
|
2073
|
+
} : {},
|
|
2074
|
+
AGENTS: {
|
|
2075
|
+
source: {
|
|
2076
|
+
type: "manyToMany",
|
|
2077
|
+
junctionTable: "agent_client",
|
|
2078
|
+
localKey: "client_id",
|
|
2079
|
+
remoteKey: "agent_id",
|
|
2080
|
+
remoteTable: "agents"
|
|
2081
|
+
},
|
|
2082
|
+
render: (rows) => {
|
|
2083
|
+
if (!rows.length) return "";
|
|
2084
|
+
const lines = rows.map(
|
|
2085
|
+
(r) => `- **${r.name}** (${r.role ?? "agent"})`
|
|
2086
|
+
);
|
|
2087
|
+
return `# Assigned Agents
|
|
2088
|
+
|
|
2089
|
+
${lines.join("\n")}
|
|
2090
|
+
`;
|
|
2091
|
+
},
|
|
2092
|
+
omitIfEmpty: true
|
|
2093
|
+
},
|
|
2094
|
+
"INVOICES.md": {
|
|
2095
|
+
source: {
|
|
2096
|
+
type: "hasMany",
|
|
2097
|
+
table: "invoice",
|
|
2098
|
+
foreignKey: "client_id"
|
|
2099
|
+
},
|
|
2100
|
+
render: (rows) => {
|
|
2101
|
+
if (!rows.length) return "";
|
|
2102
|
+
const lines = rows.map((r) => {
|
|
2103
|
+
const amt = r.amount_cents ? `$${(r.amount_cents / 100).toFixed(2)}` : "TBD";
|
|
2104
|
+
return `- **${r.number ?? "Draft"}** \u2014 ${amt} (${r.status})${r.description ? ": " + r.description : ""}`;
|
|
2105
|
+
});
|
|
2106
|
+
return `# Invoices
|
|
2107
|
+
|
|
2108
|
+
${lines.join("\n")}
|
|
2109
|
+
`;
|
|
2110
|
+
},
|
|
2111
|
+
omitIfEmpty: true
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
if (opts.files) {
|
|
2117
|
+
db.defineEntityContext("file", {
|
|
2118
|
+
table: "file",
|
|
2119
|
+
directory: "files",
|
|
2120
|
+
slugColumn: "name",
|
|
2121
|
+
indexFile: "files/FILES.md",
|
|
2122
|
+
files: {
|
|
2123
|
+
"FILE.md": {
|
|
2124
|
+
source: { type: "self" },
|
|
2125
|
+
render: (rows) => {
|
|
2126
|
+
const f = rows[0];
|
|
2127
|
+
if (!f) return "";
|
|
2128
|
+
return [
|
|
2129
|
+
`# ${f.name}`,
|
|
2130
|
+
"",
|
|
2131
|
+
f.mime_type ? `**Type:** ${f.mime_type}` : null,
|
|
2132
|
+
f.access_level ? `**Access:** ${f.access_level}` : null,
|
|
2133
|
+
f.file_path ? `**Path:** ${f.file_path}` : null,
|
|
2134
|
+
f.description ? `
|
|
2135
|
+
${f.description}` : null,
|
|
2136
|
+
""
|
|
2137
|
+
].filter(Boolean).join("\n");
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
if (opts.channels) {
|
|
2144
|
+
db.defineEntityContext("channel", {
|
|
2145
|
+
table: "channel",
|
|
2146
|
+
directory: "channels",
|
|
2147
|
+
slugColumn: "name",
|
|
2148
|
+
indexFile: "channels/CHANNELS.md",
|
|
2149
|
+
files: {
|
|
2150
|
+
"CHANNEL.md": {
|
|
2151
|
+
source: { type: "self" },
|
|
2152
|
+
render: (rows) => {
|
|
2153
|
+
const c = rows[0];
|
|
2154
|
+
if (!c) return "";
|
|
2155
|
+
return [
|
|
2156
|
+
`# ${c.name}`,
|
|
2157
|
+
"",
|
|
2158
|
+
c.platform ? `**Platform:** ${c.platform}` : null,
|
|
2159
|
+
c.type ? `**Type:** ${c.type}` : null,
|
|
2160
|
+
c.instructions ? `
|
|
2161
|
+
${c.instructions}` : null,
|
|
2162
|
+
""
|
|
2163
|
+
].filter(Boolean).join("\n");
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
|
|
1673
2171
|
// src/core/security/sanitizer.ts
|
|
1674
2172
|
import { Buffer as Buffer2 } from "buffer";
|
|
1675
2173
|
var DEFAULT_FIELD_LIMIT = 65535;
|
|
@@ -1753,6 +2251,44 @@ var AuditEmitter = class {
|
|
|
1753
2251
|
}
|
|
1754
2252
|
};
|
|
1755
2253
|
|
|
2254
|
+
// src/core/security/process-env.ts
|
|
2255
|
+
var DEFAULT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
2256
|
+
"PATH",
|
|
2257
|
+
"HOME",
|
|
2258
|
+
"USER",
|
|
2259
|
+
"SHELL",
|
|
2260
|
+
"LANG",
|
|
2261
|
+
"TERM",
|
|
2262
|
+
"TMPDIR",
|
|
2263
|
+
"XDG_RUNTIME_DIR",
|
|
2264
|
+
"NODE_ENV",
|
|
2265
|
+
// Git
|
|
2266
|
+
"GIT_AUTHOR_NAME",
|
|
2267
|
+
"GIT_AUTHOR_EMAIL",
|
|
2268
|
+
"GIT_COMMITTER_NAME",
|
|
2269
|
+
"GIT_COMMITTER_EMAIL",
|
|
2270
|
+
// Homebrew / system
|
|
2271
|
+
"HOMEBREW_PREFIX",
|
|
2272
|
+
"HOMEBREW_CELLAR",
|
|
2273
|
+
"HOMEBREW_REPOSITORY"
|
|
2274
|
+
]);
|
|
2275
|
+
function buildProcessEnv(allowedKeys, inject) {
|
|
2276
|
+
const allowed = new Set(DEFAULT_ALLOWED_KEYS);
|
|
2277
|
+
if (allowedKeys) {
|
|
2278
|
+
for (const k of allowedKeys) allowed.add(k);
|
|
2279
|
+
}
|
|
2280
|
+
const env = {};
|
|
2281
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
2282
|
+
if (allowed.has(key) && value !== void 0) {
|
|
2283
|
+
env[key] = value;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
if (inject) {
|
|
2287
|
+
Object.assign(env, inject);
|
|
2288
|
+
}
|
|
2289
|
+
return env;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
1756
2292
|
// src/core/update/version-utils.ts
|
|
1757
2293
|
function parseVersion(v) {
|
|
1758
2294
|
const cleaned = v.replace(/^v/, "").split("-")[0] ?? v;
|
|
@@ -3230,6 +3766,92 @@ var SecretStore = class {
|
|
|
3230
3766
|
};
|
|
3231
3767
|
}
|
|
3232
3768
|
};
|
|
3769
|
+
|
|
3770
|
+
// src/core/orchestrator/claude-stream-parser.ts
|
|
3771
|
+
function parseClaudeStream(stdout) {
|
|
3772
|
+
let sessionId = null;
|
|
3773
|
+
let model = null;
|
|
3774
|
+
let costUsd = null;
|
|
3775
|
+
let usage = null;
|
|
3776
|
+
let isError = false;
|
|
3777
|
+
let errorMessage = null;
|
|
3778
|
+
let stopReason = null;
|
|
3779
|
+
const textBlocks = [];
|
|
3780
|
+
for (const line of stdout.split("\n")) {
|
|
3781
|
+
if (!line.trim()) continue;
|
|
3782
|
+
let event;
|
|
3783
|
+
try {
|
|
3784
|
+
event = JSON.parse(line);
|
|
3785
|
+
} catch {
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const type = event.type;
|
|
3789
|
+
if (type === "system" && event.subtype === "init") {
|
|
3790
|
+
sessionId = event.session_id ?? null;
|
|
3791
|
+
model = event.model ?? null;
|
|
3792
|
+
}
|
|
3793
|
+
if (type === "assistant") {
|
|
3794
|
+
const msg = event.message;
|
|
3795
|
+
const content = msg?.content ?? event.content;
|
|
3796
|
+
if (Array.isArray(content)) {
|
|
3797
|
+
for (const block of content) {
|
|
3798
|
+
if (block.type === "text" && block.text) {
|
|
3799
|
+
textBlocks.push(block.text);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
if (type === "result") {
|
|
3805
|
+
isError = !!event.is_error;
|
|
3806
|
+
stopReason = event.stop_reason ?? null;
|
|
3807
|
+
costUsd = typeof event.total_cost_usd === "number" ? event.total_cost_usd : null;
|
|
3808
|
+
const u = event.usage;
|
|
3809
|
+
if (u) {
|
|
3810
|
+
usage = {
|
|
3811
|
+
inputTokens: u.input_tokens ?? 0,
|
|
3812
|
+
cachedInputTokens: u.cache_read_input_tokens ?? 0,
|
|
3813
|
+
outputTokens: u.output_tokens ?? 0
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3816
|
+
if (isError) {
|
|
3817
|
+
errorMessage = event.error ?? event.result ?? "Unknown error";
|
|
3818
|
+
}
|
|
3819
|
+
const resultContent = event.result;
|
|
3820
|
+
if (typeof resultContent === "string" && resultContent) {
|
|
3821
|
+
textBlocks.push(resultContent);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
return {
|
|
3826
|
+
sessionId,
|
|
3827
|
+
model,
|
|
3828
|
+
costUsd,
|
|
3829
|
+
usage,
|
|
3830
|
+
summary: textBlocks.join("\n"),
|
|
3831
|
+
isError,
|
|
3832
|
+
errorMessage,
|
|
3833
|
+
stopReason
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3836
|
+
function isMaxTurns(parsed) {
|
|
3837
|
+
return parsed.stopReason === "max_turns" || parsed.stopReason === "tool_use";
|
|
3838
|
+
}
|
|
3839
|
+
function isLoginRequired(stdout) {
|
|
3840
|
+
const patterns = [
|
|
3841
|
+
"not logged in",
|
|
3842
|
+
"login required",
|
|
3843
|
+
"authentication required",
|
|
3844
|
+
"please log in"
|
|
3845
|
+
];
|
|
3846
|
+
const lower = stdout.toLowerCase();
|
|
3847
|
+
return patterns.some((p) => lower.includes(p));
|
|
3848
|
+
}
|
|
3849
|
+
function deactivateLocalImagePaths(prompt) {
|
|
3850
|
+
return prompt.replace(
|
|
3851
|
+
/(?<=\s|^)(\/[\w./-]+\.(?:png|jpg|jpeg|gif|webp|svg))(?=\s|$)/gi,
|
|
3852
|
+
"[image-path:$1]"
|
|
3853
|
+
);
|
|
3854
|
+
}
|
|
3233
3855
|
export {
|
|
3234
3856
|
AGENT_STATUSES,
|
|
3235
3857
|
AgentRegistry,
|
|
@@ -3272,6 +3894,7 @@ export {
|
|
|
3272
3894
|
areDependenciesMet,
|
|
3273
3895
|
buildAgentBindings,
|
|
3274
3896
|
buildChainOrigin,
|
|
3897
|
+
buildProcessEnv,
|
|
3275
3898
|
checkAllowlist,
|
|
3276
3899
|
checkChainDepth,
|
|
3277
3900
|
checkMentionGate,
|
|
@@ -3279,8 +3902,11 @@ export {
|
|
|
3279
3902
|
classifyUpdate,
|
|
3280
3903
|
compareVersions,
|
|
3281
3904
|
createConfigRevision,
|
|
3905
|
+
deactivateLocalImagePaths,
|
|
3282
3906
|
defineCoreEntityContexts,
|
|
3283
3907
|
defineCoreTables,
|
|
3908
|
+
defineDomainEntityContexts,
|
|
3909
|
+
defineDomainTables,
|
|
3284
3910
|
detectCycle,
|
|
3285
3911
|
discoverChannels,
|
|
3286
3912
|
discoverProviders,
|
|
@@ -3289,7 +3915,10 @@ export {
|
|
|
3289
3915
|
initConfig,
|
|
3290
3916
|
interpolate,
|
|
3291
3917
|
interpolateEnv,
|
|
3918
|
+
isLoginRequired,
|
|
3919
|
+
isMaxTurns,
|
|
3292
3920
|
loadConfig,
|
|
3921
|
+
parseClaudeStream,
|
|
3293
3922
|
parseVersion,
|
|
3294
3923
|
runPackageMigrations,
|
|
3295
3924
|
sanitize,
|