toga-ai 1.0.23 → 1.0.25
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.
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/_underscore.php
|
|
@@ -73,6 +73,98 @@ multi-tenant client-DB lookup from Core. `Query.php` wraps MySQLi with read/writ
|
|
|
73
73
|
auto-routing and result helpers: `fetchRows()`, `fetchRow()`, `fetchOne()`,
|
|
74
74
|
`fetchRowsAssocArray()`, `fetchKeyValuePairs()`, `fetchValues()`.
|
|
75
75
|
|
|
76
|
+
### Physical infrastructure
|
|
77
|
+
|
|
78
|
+
Each **environment** is one or more MySQL clusters. Every cluster has a single
|
|
79
|
+
**writer** host and three **reader** hosts, one per AWS region. The framework routes
|
|
80
|
+
reads to the reader nearest the request's origin and sends all writes to the writer:
|
|
81
|
+
|
|
82
|
+
| Region constant | AWS region | Reader role |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| region 1 | `us-east-1` | `reader1.*` |
|
|
85
|
+
| region 2 | `us-west-2` | `reader2.*` |
|
|
86
|
+
| region 3 | `eu-west-1` | `reader3.*` |
|
|
87
|
+
|
|
88
|
+
#### Production clusters
|
|
89
|
+
|
|
90
|
+
Production (`prod` / `production`) splits its databases across **four dedicated
|
|
91
|
+
clusters** by database family. Each has a writer + three regional readers under its own
|
|
92
|
+
DNS subdomain (`*.<family>.database.togahub.com`):
|
|
93
|
+
|
|
94
|
+
| Cluster | Schema group | Databases served | DNS host pattern |
|
|
95
|
+
|---|---|---|---|
|
|
96
|
+
| `prod-core` | platform | `Core`, `Forecast`, `Forecast_Archive`, `Team` | `writer.core…` / `reader{1,2,3}.core.database.togahub.com` |
|
|
97
|
+
| `prod-client` | tenants | `Client_<Tenant>` (active client data) | `writer.client…` / `reader{1,2,3}.client.database.togahub.com` |
|
|
98
|
+
| `prod-archive` | archive | `Archive_<Tenant>` (historical subset of `Client_`) | `writer.archive…` / `reader{1,2,3}.archive.database.togahub.com` |
|
|
99
|
+
| `prod-logs` | tenant_logs | `Logs_<Tenant>` + base `Logs` | `writer.logs…` / `reader{1,2,3}.logs.database.togahub.com` |
|
|
100
|
+
|
|
101
|
+
**`Core` is the most important database** — it holds the common tables shared across
|
|
102
|
+
the whole platform (clients, domains, environments, ACL, parameters, APIs, records,
|
|
103
|
+
record scripts, payload interceptors).
|
|
104
|
+
|
|
105
|
+
#### Database families & tenant resolution
|
|
106
|
+
|
|
107
|
+
Within an environment, databases follow a strict naming convention. The **tenant
|
|
108
|
+
string** has a single leading capital, rest lower-case (e.g. `Towfoundation`):
|
|
109
|
+
|
|
110
|
+
| Family | Pattern | Contents |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| Platform | `Core`, `Forecast`, `Team` | Shared infrastructure (not tenant-scoped). |
|
|
113
|
+
| Tenant (active) | `Client_<Tenant>` | Live working data for one client — e.g. `Client_Towfoundation`. |
|
|
114
|
+
| Archive | `Archive_<Tenant>` | Old archived rows; **same table schema** as the matching `Client_` DB. |
|
|
115
|
+
| Tenant logs | `Logs_<Tenant>` | Per-client activity/audit logs. |
|
|
116
|
+
| Framework logs | `Logs` | Generic 2.0 logs not tied to any client. |
|
|
117
|
+
|
|
118
|
+
**Resolving a tenant string from a client name:** query `Core.Clients`. The
|
|
119
|
+
`clientIdentifier` field **is** the exact suffix used in `Client_`, `Archive_`, and
|
|
120
|
+
`Logs_` database names; the `name` field is the human-readable label. Look up by either
|
|
121
|
+
to find the other:
|
|
122
|
+
|
|
123
|
+
```sql
|
|
124
|
+
SELECT
|
|
125
|
+
name,
|
|
126
|
+
clientIdentifier
|
|
127
|
+
FROM Clients
|
|
128
|
+
WHERE
|
|
129
|
+
name LIKE '%<keyword>%'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
A client whose `clientIdentifier` is `Towfoundation` therefore lives in
|
|
133
|
+
`Client_Towfoundation`, `Archive_Towfoundation`, and `Logs_Towfoundation`.
|
|
134
|
+
|
|
135
|
+
#### Non-production environments
|
|
136
|
+
|
|
137
|
+
All non-production environments are **all-in-one**: a single cluster per environment
|
|
138
|
+
holds `Core`, `Client_<Tenant>`, `Archive_<Tenant>`, and `Logs_<Tenant>` together
|
|
139
|
+
(no family split). Region/reader routing still applies per cluster.
|
|
140
|
+
|
|
141
|
+
| Environment (aliases) | Host |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `dev-sandbox` / `sandbox-dev` / developer sandbox | `dev.sandbox.database.togahub.com` |
|
|
144
|
+
| `client-sandbox` / `sandbox-client` | `client.sandbox.database.togahub.com` |
|
|
145
|
+
| `client-alpha` / `alpha-client` | `alpha.client.database.togahub.com` |
|
|
146
|
+
| `client-beta` / `beta-client` | `beta.client.database.togahub.com` |
|
|
147
|
+
| `client-gamma` / `gamma-client` | `gamma.client.database.togahub.com` |
|
|
148
|
+
| `qa-task` | `task.qa.database.togahub.com` |
|
|
149
|
+
| `qa-hotfix` | `hotfix.qa.database.togahub.com` |
|
|
150
|
+
| `qa-alpha` | `alpha.qa.database.togahub.com` |
|
|
151
|
+
| `qa-beta` | `beta.qa.database.togahub.com` |
|
|
152
|
+
| `qa-gamma` | `gamma.qa.database.togahub.com` |
|
|
153
|
+
| `qc-task` | `task.qc.database.togahub.com` |
|
|
154
|
+
| `qc-hotfix` | `hotfix.qc.database.togahub.com` |
|
|
155
|
+
| `qc-security` | `security.qc.database.togahub.com` |
|
|
156
|
+
| `qc-performance` | `performance.qc.database.togahub.com` |
|
|
157
|
+
| `qc-alpha` | `alpha.qc.database.togahub.com` |
|
|
158
|
+
| `qc-beta` | `beta.qc.database.togahub.com` |
|
|
159
|
+
| `qc-gamma` | `gamma.qc.database.togahub.com` |
|
|
160
|
+
| `stage` / `staging` | `stage.database.togahub.com` |
|
|
161
|
+
| `demo` | `demo.database.togahub.com` |
|
|
162
|
+
|
|
163
|
+
> **Credentials** are managed via the database/MCP environment config — **never stored
|
|
164
|
+
> in this repo or in any committed file** (per `rules/common/security.md` and
|
|
165
|
+
> `git-workflow.md`). Do not paste DB usernames or passwords into knowledge docs, code,
|
|
166
|
+
> or commits.
|
|
167
|
+
|
|
76
168
|
## Model layer
|
|
77
169
|
|
|
78
170
|
Each DB table has a model. Every model must define:
|
|
@@ -3,5 +3,3 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [API (api2 / TOGa API v2) Architecture](architecture.md) | `api2` is the backend powering the public **TOGa 2.0 API**. | api2/Controller/Index.php, api2/Component/Api/V2/V2.php, api2/Component/Api/Cxml/Cxml.php, api2/Component/Api/V2/Response/Response.php, api2/Config/ |
|
|
6
|
-
| [Error Response Envelope](features/error-response.md) | Every api2 action method returns the same three-key envelope. | api2/Controller/Index.php |
|
|
7
|
-
| [Health Check Endpoint](features/health-endpoint.md) | `GET /health` returns HTTP 200 with an empty JSON object (`{}`). | api2/Controller/Index.php |
|
package/knowledge/INDEX.md
CHANGED
|
@@ -11,7 +11,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
11
11
|
|
|
12
12
|
- **_underscore** (_Underscore) _(framework core)_ — 2 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
13
13
|
- **worker2** (Worker) — 3 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
14
|
-
- **api2** (API) —
|
|
14
|
+
- **api2** (API) — 1 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
15
15
|
|
|
16
16
|
## Clients
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toga-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"homepage": "https://togatech.com",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "https://github.com/
|
|
18
|
+
"url": "https://github.com/agilantsolutions/claude"
|
|
19
19
|
},
|
|
20
20
|
"license": "UNLICENSED",
|
|
21
21
|
"private": false,
|
package/scripts/install.js
CHANGED
|
@@ -510,16 +510,15 @@ function main() {
|
|
|
510
510
|
|
|
511
511
|
const bundleVersion = getBundleVersion();
|
|
512
512
|
|
|
513
|
-
//
|
|
514
|
-
//
|
|
513
|
+
// Warn if npx served a stale cached version (common on Windows). Never block — install
|
|
514
|
+
// the current bundle now and let the user update afterwards.
|
|
515
515
|
if (!isPostInstall) {
|
|
516
516
|
const latestVersion = getLatestNpmVersion();
|
|
517
517
|
if (latestVersion && semverLt(bundleVersion, latestVersion)) {
|
|
518
518
|
console.log('');
|
|
519
|
-
console.log('
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
process.exit(upgrade.status ?? 0);
|
|
519
|
+
console.log(' ⚠ Running toga-ai@' + bundleVersion + ' — toga-ai@' + latestVersion + ' is available.');
|
|
520
|
+
console.log(' Update with: npm cache clean --force && npx toga-ai@latest');
|
|
521
|
+
console.log(' (Continuing install with current bundle...)');
|
|
523
522
|
}
|
|
524
523
|
}
|
|
525
524
|
|
|
@@ -673,12 +672,28 @@ function main() {
|
|
|
673
672
|
|
|
674
673
|
// Knowledge — git repo if available (has team-captured docs), else npm bundle
|
|
675
674
|
// .claude/knowledge/ is a mirror — update when content changes so teammates get captured docs
|
|
676
|
-
const knowledgeSrc = path.join(knowledgeDir, 'knowledge');
|
|
677
675
|
const knowledgeDest = path.join(claudeDir, 'knowledge');
|
|
678
676
|
let knowledgeStats = { added: 0, updated: 0, unchanged: 0 };
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
677
|
+
|
|
678
|
+
// Always install npm bundle knowledge first — this guarantees every file published
|
|
679
|
+
// in this version is present, regardless of what the git repo contains.
|
|
680
|
+
const bundleKnowledgeSrc = path.join(PACKAGE_ROOT, 'knowledge');
|
|
681
|
+
if (fs.existsSync(bundleKnowledgeSrc)) {
|
|
682
|
+
try { knowledgeStats = copyDir(bundleKnowledgeSrc, knowledgeDest, { updateIfChanged: true }); }
|
|
683
|
+
catch (e) { errors.push('knowledge (bundle): ' + e.message); }
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Then overlay git repo on top — picks up any docs newer than this npm bundle.
|
|
687
|
+
// Git is additive only: it never removes files the bundle already installed.
|
|
688
|
+
if (knowledgeDir !== PACKAGE_ROOT) {
|
|
689
|
+
const gitKnowledgeSrc = path.join(knowledgeDir, 'knowledge');
|
|
690
|
+
if (fs.existsSync(gitKnowledgeSrc)) {
|
|
691
|
+
try {
|
|
692
|
+
const gitStats = copyDir(gitKnowledgeSrc, knowledgeDest, { updateIfChanged: true });
|
|
693
|
+
knowledgeStats.added += gitStats.added;
|
|
694
|
+
knowledgeStats.updated += gitStats.updated;
|
|
695
|
+
} catch (e) { /* non-fatal — bundle layer already installed */ }
|
|
696
|
+
}
|
|
682
697
|
}
|
|
683
698
|
console.log(' ✓ Knowledge (' + countMd(knowledgeDest) + ' docs): ' + fmtStats(knowledgeStats));
|
|
684
699
|
|