@wipcomputer/wip-ldm-os 0.4.57 → 0.4.58

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/SKILL.md CHANGED
@@ -9,7 +9,7 @@ license: MIT
9
9
  compatibility: Requires git, npm, node. Node.js 18+.
10
10
  metadata:
11
11
  display-name: "LDM OS"
12
- version: "0.4.57"
12
+ version: "0.4.58"
13
13
  homepage: "https://github.com/wipcomputer/wip-ldm-os"
14
14
  author: "Parker Todd Brooks"
15
15
  category: infrastructure
package/bin/ldm.js CHANGED
@@ -1791,6 +1791,57 @@ function cmdStatus() {
1791
1791
 
1792
1792
  // ── ldm sessions ──
1793
1793
 
1794
+ // ── ldm backup ──
1795
+
1796
+ async function cmdBackup() {
1797
+ const BACKUP_SCRIPT = join(LDM_ROOT, 'bin', 'ldm-backup.sh');
1798
+
1799
+ if (!existsSync(BACKUP_SCRIPT)) {
1800
+ console.error(' x Backup script not found at ' + BACKUP_SCRIPT);
1801
+ console.error(' Run: ldm install (deploys the backup script)');
1802
+ process.exit(1);
1803
+ }
1804
+
1805
+ const backupArgs = [];
1806
+ if (DRY_RUN) backupArgs.push('--dry-run');
1807
+
1808
+ // --pin: mark the latest backup to skip rotation
1809
+ const pinIndex = args.indexOf('--pin');
1810
+ if (pinIndex !== -1) {
1811
+ const reason = args[pinIndex + 1] || 'pinned';
1812
+ // Find latest backup dir
1813
+ const backupRoot = join(LDM_ROOT, 'backups');
1814
+ const dirs = readdirSync(backupRoot)
1815
+ .filter(d => d.match(/^20\d\d-\d\d-\d\d--/))
1816
+ .sort()
1817
+ .reverse();
1818
+ if (dirs.length === 0) {
1819
+ console.error(' x No backups found to pin.');
1820
+ process.exit(1);
1821
+ }
1822
+ const latest = dirs[0];
1823
+ const pinFile = join(backupRoot, latest, '.pinned');
1824
+ writeFileSync(pinFile, `Pinned: ${reason}\nDate: ${new Date().toISOString()}\n`);
1825
+ console.log(` + Pinned backup ${latest}: ${reason}`);
1826
+ console.log(' This backup will be skipped during rotation.');
1827
+ return;
1828
+ }
1829
+
1830
+ console.log(' Running backup...');
1831
+ console.log('');
1832
+ try {
1833
+ execSync(`bash "${BACKUP_SCRIPT}" ${backupArgs.join(' ')}`, {
1834
+ stdio: 'inherit',
1835
+ timeout: 600000,
1836
+ });
1837
+ } catch (e) {
1838
+ console.error(' x Backup failed: ' + e.message);
1839
+ process.exit(1);
1840
+ }
1841
+ }
1842
+
1843
+ // ── ldm sessions ──
1844
+
1794
1845
  async function cmdSessions() {
1795
1846
  const { listSessions } = await import('../lib/sessions.mjs');
1796
1847
  const sessions = listSessions({ includeStale: CLEANUP_FLAG });
@@ -2842,6 +2893,9 @@ async function main() {
2842
2893
  case 'worktree':
2843
2894
  await cmdWorktree();
2844
2895
  break;
2896
+ case 'backup':
2897
+ await cmdBackup();
2898
+ break;
2845
2899
  default:
2846
2900
  console.error(` Unknown command: ${command}`);
2847
2901
  console.error(` Run: ldm --help`);
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wipcomputer/wip-ldm-os",
3
- "version": "0.4.57",
3
+ "version": "0.4.58",
4
4
  "type": "module",
5
5
  "description": "LDM OS: identity, memory, and sovereignty infrastructure for AI agents",
6
6
  "engines": {
@@ -193,16 +193,26 @@ echo "--- ~/.claude/ ---"
193
193
 
194
194
  if [ -n "$WORKSPACE" ] && [ -d "$WORKSPACE" ]; then
195
195
  echo "--- $WORKSPACE/ ---"
196
- tar -cf "$DEST/wipcomputerinc.tar" \
197
- --exclude "node_modules" \
198
- --exclude ".git/objects" \
199
- --exclude ".DS_Store" \
200
- --exclude "*/staff/cc-mini/documents/backups" \
201
- --exclude "*/_temp/backups" \
202
- --exclude "*/_trash" \
203
- -C "$(dirname "$WORKSPACE")" "$(basename "$WORKSPACE")" 2>/dev/null \
204
- && echo " workspace: tar OK" \
205
- || echo " workspace: tar FAILED"
196
+
197
+ # Size guard: estimate workspace size before tarring
198
+ ESTIMATED_KB=$(du -sk --exclude="node_modules" --exclude=".git" --exclude="_temp/_archive" --exclude="_trash" "$WORKSPACE" 2>/dev/null | cut -f1 || echo "0")
199
+ MAX_KB=10000000 # 10GB
200
+ if [ "$ESTIMATED_KB" -gt "$MAX_KB" ] 2>/dev/null; then
201
+ echo " ERROR: Workspace estimated at ${ESTIMATED_KB}KB (>10GB). Aborting tar to prevent disk fill."
202
+ echo " Check for large directories: du -sh $WORKSPACE/*/"
203
+ else
204
+ tar -cf "$DEST/wipcomputerinc.tar" \
205
+ --exclude "node_modules" \
206
+ --exclude ".git/objects" \
207
+ --exclude ".DS_Store" \
208
+ --exclude "*/staff/cc-mini/documents/backups" \
209
+ --exclude "*/_temp/backups" \
210
+ --exclude "*/_temp/_archive" \
211
+ --exclude "*/_trash" \
212
+ -C "$(dirname "$WORKSPACE")" "$(basename "$WORKSPACE")" 2>/dev/null \
213
+ && echo " workspace: tar OK (est ${ESTIMATED_KB}KB)" \
214
+ || echo " workspace: tar FAILED"
215
+ fi
206
216
  fi
207
217
 
208
218
  # ── 5. iCloud offsite ──
@@ -235,6 +245,11 @@ BACKUP_COUNT=$(ls -1d "$BACKUP_ROOT"/20??-??-??--* 2>/dev/null | wc -l | tr -d '
235
245
  if [ "$BACKUP_COUNT" -gt "$KEEP" ]; then
236
246
  REMOVE_COUNT=$((BACKUP_COUNT - KEEP))
237
247
  ls -1d "$BACKUP_ROOT"/20??-??-??--* | head -n "$REMOVE_COUNT" | while read OLD; do
248
+ # Skip pinned backups
249
+ if [ -f "$OLD/.pinned" ]; then
250
+ echo " Skipped (pinned): $(basename "$OLD")"
251
+ continue
252
+ fi
238
253
  rm -rf "$OLD"
239
254
  echo " Removed: $(basename "$OLD")"
240
255
  done