create-ironclaws 1.0.1 → 1.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ironclaws",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Create an IronClaws AI agent platform in seconds",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,6 +22,20 @@ Every message you send goes to Slack. Slack does not render standard Markdown. F
22
22
 
23
23
  ---
24
24
 
25
+ ## RAG Status Emoji — Exact Codes (memorise these)
26
+
27
+ These are the ONLY correct codes for traffic-light status indicators. Do not guess or vary them.
28
+
29
+ | Status | Correct code | Wrong codes (do not use) |
30
+ |--------|-------------|--------------------------|
31
+ | 🔴 Red / violation / critical | `:red_circle:` | ~~`:large_red_circle:`~~ |
32
+ | 🟡 Yellow / warning / caution | `:large_yellow_circle:` | ~~`:yellow_circle:`~~ ~~`:warning:`~~ |
33
+ | 🟢 Green / compliant / clean | `:large_green_circle:` | ~~`:green_circle:`~~ ~~`:white_check_mark:`~~ |
34
+
35
+ Rule: red has no `large_` prefix. Yellow and green both require the `large_` prefix. This is a Slack quirk — the non-large versions of yellow and green render as different emoji entirely.
36
+
37
+ ---
38
+
25
39
  ## Data without tables
26
40
 
27
41
  Tables don't render. Use these patterns instead:
@@ -4,6 +4,8 @@ You are Forge, the built-in guide for IronClaws. You help people understand how
4
4
 
5
5
  You are running inside IronClaws itself — which means you can inspect the codebase, read configuration files, and help make changes directly.
6
6
 
7
+ **You communicate exclusively through Slack. Always use Slack mrkdwn formatting — never standard Markdown. Follow the `slack-formatting` skill for every message you send. Key rules: `*bold*` not `**bold**`, no `##` headings, no markdown tables, no `---` dividers.**
8
+
7
9
  ---
8
10
 
9
11
  ## What you know
@@ -7,7 +7,7 @@
7
7
  # Run this on the VM after deploying NanoClaw:
8
8
  # cd ~/nanoclaw-docker && bash scripts/setup-onecli-secrets.sh
9
9
 
10
- set -euo pipefail
10
+ set -eo pipefail
11
11
 
12
12
  ENV_FILE="${ENV_FILE:-$(dirname "$0")/../.env}"
13
13
  API="http://localhost:10254"
@@ -112,6 +112,13 @@ if [ ! -f "$ENV_FILE" ]; then
112
112
  exit 1
113
113
  fi
114
114
 
115
+ # Check OneCLI is reachable before proceeding
116
+ if ! api_curl "$API/api/secrets" > /dev/null 2>&1; then
117
+ echo " ⚠ OneCLI not reachable at $API — skipping secrets setup"
118
+ echo " Start OneCLI with: docker compose up -d"
119
+ exit 0
120
+ fi
121
+
115
122
  echo "Reading credentials from $ENV_FILE"
116
123
  echo ""
117
124
 
@@ -135,6 +142,13 @@ CONFLUENCE_BASIC=$(printf '%s:%s' "$CONFLUENCE_USERNAME" "$CONFLUENCE_PASSWORD"
135
142
  SLACK_BOT_TOKEN=$(env_val "SLACK_BOT_TOKEN")
136
143
  INTERCOM_ACCESS_TOKEN=$(env_val "INTERCOM_ACCESS_TOKEN")
137
144
 
145
+ XLEDGER_API_TOKEN=$(env_val "XLEDGER_API_TOKEN")
146
+ XLEDGER_API_BASE=$(env_val "XLEDGER_API_BASE")
147
+ XLEDGER_HOST=$(echo "$XLEDGER_API_BASE" | sed 's|^https://||;s|^http://||' | cut -d/ -f1)
148
+
149
+ ARDOQ_API_KEY=$(env_val "ARDOQ_API_KEY")
150
+ ARDOQ_BASE_URL=$(env_val "ARDOQ_BASE_URL")
151
+ ARDOQ_HOST=$(echo "$ARDOQ_BASE_URL" | sed 's|^https://||;s|^http://||' | cut -d/ -f1)
138
152
 
139
153
  ANTHROPIC_AUTH_TOKEN=$(env_val "ANTHROPIC_AUTH_TOKEN")
140
154
  LLM_GATEWAY=$(env_val "ANTHROPIC_BASE_URL")
@@ -168,11 +182,18 @@ fi
168
182
  upsert_secret "intercom" "api.intercom.io" "Authorization" "Bearer $INTERCOM_ACCESS_TOKEN" || \
169
183
  echo " ⚠ Skipping intercom"
170
184
 
185
+ [ -n "$ARDOQ_API_KEY" ] && [ -n "$ARDOQ_HOST" ] && \
186
+ upsert_secret "ardoq" "$ARDOQ_HOST" "Authorization" "Token token=$ARDOQ_API_KEY" || \
187
+ echo " ⚠ Skipping ardoq"
171
188
 
172
189
  [ -n "$ANTHROPIC_AUTH_TOKEN" ] && [ -n "$LLM_GATEWAY_HOST" ] && \
173
190
  upsert_secret "litellm" "$LLM_GATEWAY_HOST" "Authorization" "Bearer $ANTHROPIC_AUTH_TOKEN" || \
174
191
  echo " ⚠ Skipping litellm"
175
192
 
193
+ [ -n "$XLEDGER_API_TOKEN" ] && [ -n "$XLEDGER_HOST" ] && \
194
+ upsert_secret "xledger" "$XLEDGER_HOST" "Authorization" "token $XLEDGER_API_TOKEN" || \
195
+ echo " ⚠ Skipping xledger"
196
+
176
197
  echo ""
177
198
 
178
199
  # ── Register global-claw agent if missing ────────────────────────────────────
@@ -219,7 +240,14 @@ if ! python3 -c "import yaml" 2>/dev/null; then
219
240
  pip3 install pyyaml --quiet --break-system-packages 2>/dev/null || \
220
241
  pip3 install pyyaml --quiet 2>/dev/null || \
221
242
  pip install pyyaml --quiet 2>/dev/null || \
222
- { echo "ERROR: Could not install PyYAML. Run: pip3 install pyyaml"; exit 1; }
243
+ { echo " Could not install PyYAML — skipping agent secret linking. Run: pip3 install pyyaml"; SKIP_LINKING=1; }
244
+ fi
245
+
246
+ if [ "${SKIP_LINKING:-0}" = "1" ]; then
247
+ echo " ⚠ Skipping agent secret linking (PyYAML unavailable)"
248
+ echo ""
249
+ echo "Done."
250
+ exit 0
223
251
  fi
224
252
 
225
253
  echo "Linking secrets to agents (from agents.yaml)..."
@@ -647,6 +647,7 @@ function autoRegisterAgentsFromYaml(): void {
647
647
  is_main?: boolean;
648
648
  onecli_secrets?: string[];
649
649
  onecli_id?: string;
650
+ container_config?: import('./types.js').ContainerConfig;
650
651
  }>;
651
652
 
652
653
  try {
@@ -676,6 +677,7 @@ function autoRegisterAgentsFromYaml(): void {
676
677
  added_at: new Date().toISOString(),
677
678
  requiresTrigger: def.requires_trigger !== false,
678
679
  isMain: def.is_main === true,
680
+ ...(def.container_config ? { containerConfig: def.container_config } : {}),
679
681
  };
680
682
 
681
683
  setRegisteredGroup(jid, group);
@@ -714,13 +716,32 @@ function ensureOneCLISecrets(): void {
714
716
  const secretsScript = path.join(process.cwd(), 'scripts', 'setup-onecli-secrets.sh');
715
717
  if (!fs.existsSync(secretsScript)) return;
716
718
 
717
- // Hash the credential-bearing env vars that the secrets script uses
719
+ // Hash two things:
720
+ // 1. Credential-bearing env vars (re-run when secrets rotate)
721
+ // 2. onecli_secrets from agents.yaml (re-run when an agent's secret list changes)
718
722
  const credentialKeys = [
719
723
  'ELASTIC_API_KEY', 'JIRA_EMAIL', 'JIRA_API_TOKEN',
720
724
  'SLACK_BOT_TOKEN', 'INTERCOM_ACCESS_TOKEN', 'ARDOQ_API_KEY',
721
725
  'ANTHROPIC_AUTH_TOKEN', 'CONFLUENCE_USERNAME', 'CONFLUENCE_PASSWORD',
726
+ 'XLEDGER_API_TOKEN',
722
727
  ];
723
- const hashInput = credentialKeys.map(k => `${k}=${process.env[k] || ''}`).join('\n');
728
+ const envHash = credentialKeys.map(k => `${k}=${process.env[k] || ''}`).join('\n');
729
+
730
+ // Include agents.yaml onecli_secrets so adding/removing a secret triggers re-run
731
+ let agentsSecretsSig = '';
732
+ try {
733
+ const agentsYaml = path.join(process.cwd(), 'agents.yaml');
734
+ if (fs.existsSync(agentsYaml)) {
735
+ const defs = YAML.parse(fs.readFileSync(agentsYaml, 'utf-8')).agents || [];
736
+ agentsSecretsSig = defs
737
+ .map((d: { folder: string; onecli_secrets?: string[] }) =>
738
+ `${d.folder}:${(d.onecli_secrets || []).sort().join(',')}`)
739
+ .sort()
740
+ .join('\n');
741
+ }
742
+ } catch { /* non-fatal — env hash alone is still useful */ }
743
+
744
+ const hashInput = envHash + '\n---agents---\n' + agentsSecretsSig;
724
745
  const currentHash = crypto.createHash('sha256').update(hashInput).digest('hex');
725
746
 
726
747
  const hashFile = path.join(STORE_DIR, 'onecli-secrets.hash');