lwazi 1.15.0 → 1.16.2

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/bin/crawl.php CHANGED
@@ -14,20 +14,106 @@ if (!is_dir($storageDir)) {
14
14
  mkdir($storageDir, 0755, true);
15
15
  }
16
16
 
17
- $rootUrl = $argv[1] ?? 'http://localhost';
18
- $maxPages = (int)($argv[2] ?? 1000);
19
- $maxDepth = (int)($argv[3] ?? 10);
17
+ echo "Lwazi Web Crawler\n";
18
+ echo "==================\n\n";
20
19
 
21
- // Check for login options
22
- $loginUrl = $argv[4] ?? null;
23
- $loginUser = $argv[5] ?? null;
24
- $loginPass = $argv[6] ?? null;
20
+ $options = [
21
+ 'url' => 'http://localhost',
22
+ 'max_pages' => 1000,
23
+ 'max_depth' => 10,
24
+ 'login_url' => '',
25
+ 'login_user' => '',
26
+ 'login_pass' => '',
27
+ ];
25
28
 
26
- echo "Lwazi Web Crawler\n";
27
- echo "=================\n\n";
28
- echo "Root URL: $rootUrl\n";
29
- echo "Max pages: $maxPages\n";
30
- echo "Max depth: $maxDepth\n";
29
+ // Parse command line arguments
30
+ for ($i = 1; $i < $argc; $i++) {
31
+ $arg = $argv[$i];
32
+ if ($arg === '-h' || $arg === '--help') {
33
+ echo "Usage: php lwazi/bin/crawl.php [options]\n\n";
34
+ echo "Options:\n";
35
+ echo " -u, --url <url> Root URL to crawl (default: http://localhost)\n";
36
+ echo " -m, --max-pages <n> Maximum pages to crawl (default: 1000)\n";
37
+ echo " -d, --max-depth <n> Maximum depth (default: 10)\n";
38
+ echo " -l, --login-url <url> Login page URL\n";
39
+ echo " -e, --login-email <email> Login email/username\n";
40
+ echo " -p, --login-pass <pass> Login password\n";
41
+ echo " --url <url> Root URL to crawl (default: http://localhost)\n";
42
+ echo "\nExamples:\n";
43
+ echo " php lwazi/bin/crawl.php\n";
44
+ echo " php lwazi/bin/crawl.php --url http://localhost:8000\n";
45
+ echo " php lwazi/bin/crawl.php -u http://localhost:8000 -m 500\n";
46
+ echo " php lwazi/bin/crawl.php -u http://localhost -l http://localhost/login -e admin@example.com -p secret\n";
47
+ exit(0);
48
+ }
49
+
50
+ if (in_array($arg, ['-u', '--url']) && isset($argv[$i + 1])) {
51
+ $options['url'] = $argv[++$i];
52
+ } elseif (in_array($arg, ['-m', '--max-pages']) && isset($argv[$i + 1])) {
53
+ $options['max_pages'] = (int)$argv[++$i];
54
+ } elseif (in_array($arg, ['-d', '--max-depth']) && isset($argv[$i + 1])) {
55
+ $options['max_depth'] = (int)$argv[++$i];
56
+ } elseif (in_array($arg, ['-l', '--login-url']) && isset($argv[$i + 1])) {
57
+ $options['login_url'] = $argv[++$i];
58
+ } elseif (in_array($arg, ['-e', '--login-email', '--login-user']) && isset($argv[$i + 1])) {
59
+ $options['login_user'] = $argv[++$i];
60
+ } elseif (in_array($arg, ['-p', '--login-pass', '--login-password']) && isset($argv[$i + 1])) {
61
+ $options['login_pass'] = $argv[++$i];
62
+ } elseif (!str_starts_with($arg, '-')) {
63
+ // First non-option argument is URL
64
+ if ($options['url'] === 'http://localhost') {
65
+ $options['url'] = $arg;
66
+ }
67
+ }
68
+ }
69
+
70
+ // Interactive mode if no URL provided
71
+ if ($options['url'] === 'http://localhost' && $argc === 1) {
72
+ echo "Interactive Mode - Press Enter for defaults\n\n";
73
+
74
+ $input = readline("Root URL to crawl [http://localhost:8000]: ");
75
+ if (!empty(trim($input))) {
76
+ $options['url'] = trim($input);
77
+ } else {
78
+ $options['url'] = 'http://localhost:8000';
79
+ }
80
+
81
+ $input = readline("Max pages [1000]: ");
82
+ if (!empty(trim($input))) {
83
+ $options['max_pages'] = (int)trim($input);
84
+ }
85
+
86
+ $input = readline("Max depth [10]: ");
87
+ if (!empty(trim($input))) {
88
+ $options['max_depth'] = (int)trim($input);
89
+ }
90
+
91
+ $input = readline("Login URL (optional, press Enter to skip): ");
92
+ if (!empty(trim($input))) {
93
+ $options['login_url'] = trim($input);
94
+
95
+ if (!empty($options['login_url'])) {
96
+ $input = readline("Login email/username: ");
97
+ $options['login_user'] = trim($input);
98
+
99
+ $input = readline("Login password: ");
100
+ $options['login_pass'] = trim($input);
101
+ }
102
+ }
103
+
104
+ echo "\n";
105
+ }
106
+
107
+ echo "Root URL: {$options['url']}\n";
108
+ echo "Max pages: {$options['max_pages']}\n";
109
+ echo "Max depth: {$options['max_depth']}\n";
110
+
111
+ $rootUrl = $options['url'];
112
+ $maxPages = $options['max_pages'];
113
+ $maxDepth = $options['max_depth'];
114
+ $loginUrl = $options['login_url'];
115
+ $loginUser = $options['login_user'];
116
+ $loginPass = $options['login_pass'];
31
117
 
32
118
  $cookies = tempnam(sys_get_temp_dir(), 'lwazi_cookies_');
33
119
 
@@ -112,7 +198,6 @@ function extractMeta(string $html, string $name): string {
112
198
  }
113
199
 
114
200
  function extractCsrfToken(string $html): ?string {
115
- // Look for Laravel CSRF token
116
201
  if (preg_match('/name="_token"\s+value="([^"]+)"/', $html, $m)) return $m[1];
117
202
  if (preg_match('/name="_token"\s*value="([^"]+)"/', $html, $m)) return $m[1];
118
203
  if (preg_match('/meta\s+name="csrf-token"\s+content="([^"]+)"/', $html, $m)) return $m[1];
@@ -122,7 +207,6 @@ function extractCsrfToken(string $html): ?string {
122
207
  function loginAndGetCookies(string $loginUrl, string $username, string $password, string $cookies): bool {
123
208
  echo "Attempting to login to $loginUrl...\n";
124
209
 
125
- // First, get the login page to extract CSRF token
126
210
  $ch = curl_init();
127
211
  curl_setopt($ch, CURLOPT_URL, $loginUrl);
128
212
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@@ -141,15 +225,11 @@ function loginAndGetCookies(string $loginUrl, string $username, string $password
141
225
  return false;
142
226
  }
143
227
 
144
- // Extract CSRF token
145
228
  $csrfToken = extractCsrfToken($loginPage);
146
229
 
147
- // Determine login field names (common patterns)
148
230
  $emailField = 'email';
149
231
  $passwordField = 'password';
150
- $usernameField = 'username';
151
232
 
152
- // Try to detect field names from the form
153
233
  if (preg_match('/name="(email|user|username|login)"[^>]*>/i', $loginPage, $m)) {
154
234
  $emailField = $m[1];
155
235
  }
@@ -157,7 +237,6 @@ function loginAndGetCookies(string $loginUrl, string $username, string $password
157
237
  $passwordField = $m[1];
158
238
  }
159
239
 
160
- // Build login data
161
240
  $loginData = [
162
241
  $emailField => $username,
163
242
  $passwordField => $password,
@@ -167,12 +246,8 @@ function loginAndGetCookies(string $loginUrl, string $username, string $password
167
246
  $loginData['_token'] = $csrfToken;
168
247
  }
169
248
 
170
- // Try common login field combinations
171
- $postUrl = $loginUrl;
172
-
173
- // Submit login
174
249
  $ch = curl_init();
175
- curl_setopt($ch, CURLOPT_URL, $postUrl);
250
+ curl_setopt($ch, CURLOPT_URL, $loginUrl);
176
251
  curl_setopt($ch, CURLOPT_POST, true);
177
252
  curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($loginData));
178
253
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
@@ -187,7 +262,6 @@ function loginAndGetCookies(string $loginUrl, string $username, string $password
187
262
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
188
263
  curl_close($ch);
189
264
 
190
- // Check if login was successful (usually redirects to dashboard or returns 200)
191
265
  if ($httpCode >= 200 && $httpCode < 400) {
192
266
  echo "Login successful!\n";
193
267
  return true;
@@ -197,18 +271,16 @@ function loginAndGetCookies(string $loginUrl, string $username, string $password
197
271
  return false;
198
272
  }
199
273
 
200
- // Login if credentials provided
201
274
  if ($loginUrl && $loginUser && $loginPass) {
202
275
  $loggedIn = loginAndGetCookies($loginUrl, $loginUser, $loginPass, $cookies);
203
276
  if (!$loggedIn) {
204
277
  echo "Warning: Login may have failed. Continuing anyway...\n";
205
278
  }
206
- } else {
207
- echo "\nTip: To crawl authenticated pages, provide login credentials:\n";
208
- echo " php lwazi/bin/crawl.php <url> [max_pages] [max_depth] <login_url> <username> <password>\n\n";
279
+ } elseif ($loginUrl && !$loginUser) {
280
+ echo "Login URL provided but no credentials. Skipping login.\n";
209
281
  }
210
282
 
211
- echo "Starting crawl...\n\n";
283
+ echo "\nStarting crawl...\n\n";
212
284
 
213
285
  $visited = [];
214
286
  $queue = [[$rootUrl, 0]];
@@ -250,7 +322,6 @@ while (!empty($queue) && $totalPages < $maxPages) {
250
322
  continue;
251
323
  }
252
324
 
253
- // Check for login redirect
254
325
  if (stripos($html, 'login') !== false && stripos($html, 'password') !== false) {
255
326
  echo " -> Login required, skipping\n";
256
327
  continue;
@@ -284,7 +355,6 @@ while (!empty($queue) && $totalPages < $maxPages) {
284
355
 
285
356
  echo "\nCrawling complete! Found $totalPages pages.\n";
286
357
 
287
- // Save manifest
288
358
  $manifest = [
289
359
  'root_url' => $rootUrl,
290
360
  'crawled_at' => date('c'),
@@ -296,12 +366,10 @@ $manifest = [
296
366
  file_put_contents($manifestFile, json_encode($manifest, JSON_PRETTY_PRINT));
297
367
  echo "Saved manifest to lwazi/manifest.json\n";
298
368
 
299
- // Save page data for later indexing
300
369
  $indexFile = $storageDir . '/crawl_data.json';
301
370
  file_put_contents($indexFile, json_encode($pageData));
302
371
  echo "Saved page data to storage/lwazi/crawl_data.json\n";
303
372
 
304
- // Cleanup
305
373
  @unlink($cookies);
306
374
 
307
375
  echo "\nDone! You can now use lwazi with your knowledge base.\n";
package/bin/uninstall.js CHANGED
@@ -100,6 +100,13 @@ function manualCleanup() {
100
100
  console.log(colors.green('✓') + ' Removed lwazi directory');
101
101
  }
102
102
 
103
+ // Remove storage/lwazi directory
104
+ const storageLwazi = path.join(projectRoot, 'storage', 'lwazi');
105
+ if (fs.existsSync(storageLwazi)) {
106
+ fs.rmSync(storageLwazi, { recursive: true, force: true });
107
+ console.log(colors.green('✓') + ' Removed storage/lwazi directory');
108
+ }
109
+
103
110
  // Remove from composer.json
104
111
  const composerJson = path.join(projectRoot, 'composer.json');
105
112
  if (fs.existsSync(composerJson)) {
package/bin/update.js CHANGED
@@ -125,6 +125,15 @@ try {
125
125
  }
126
126
 
127
127
  console.log("\nLwazi updated successfully!");
128
- console.log("\nCLI Tools:");
129
- console.log(" - php lwazi/bin/crawl.php <url> # Crawl a website");
130
- console.log(" - php lwazi/bin/analyze.php # Re-analyze project");
128
+ console.log("\n========================================");
129
+ console.log(" Available Commands");
130
+ console.log("========================================");
131
+ console.log("\n1. Crawl your website (builds page map):");
132
+ console.log(" php lwazi/bin/crawl.php # Interactive mode");
133
+ console.log(" php lwazi/bin/crawl.php -u http://localhost:8000");
134
+ console.log(" php lwazi/bin/crawl.php --url http://localhost --help");
135
+ console.log("\n2. Re-analyze project (updates routes/models):");
136
+ console.log(" php lwazi/bin/analyze.php");
137
+ console.log("\n3. Crawl with login (for authenticated pages):");
138
+ console.log(" php lwazi/bin/crawl.php -u http://localhost -l /login -e user@email.com -p password");
139
+ console.log("\n========================================");
package/install CHANGED
@@ -214,9 +214,22 @@ if [ "$COMPOSER_USED" = true ]; then
214
214
  fi
215
215
 
216
216
  echo ""
217
- echo "CLI Tools:"
218
- echo " - php lwazi/bin/crawl.php <url> # Crawl a website"
219
- echo " - php lwazi/bin/analyze.php # Re-analyze project"
217
+ echo "========================================"
218
+ echo " Available Commands"
219
+ echo "========================================"
220
+ echo ""
221
+ echo "1. Crawl your website (builds page map):"
222
+ echo " php lwazi/bin/crawl.php # Interactive mode"
223
+ echo " php lwazi/bin/crawl.php -u http://localhost:8000"
224
+ echo " php lwazi/bin/crawl.php --url http://localhost --help"
225
+ echo ""
226
+ echo "2. Re-analyze project (updates routes/models):"
227
+ echo " php lwazi/bin/analyze.php"
228
+ echo ""
229
+ echo "3. Crawl with login (for authenticated pages):"
230
+ echo " php lwazi/bin/crawl.php -u http://localhost -l /login -e user@email.com -p password"
231
+ echo ""
232
+ echo "========================================"
220
233
  echo ""
221
234
  echo "Configuration:"
222
235
  echo " - Model: $MODEL"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lwazi",
3
- "version": "1.15.0",
3
+ "version": "1.16.2",
4
4
  "description": "Lwazi is an AI assistant for Laravel. Install with one command to add an AI assistant to your Laravel app.",
5
5
  "main": "bin/lwazi.js",
6
6
  "bin": {
package/uninstall CHANGED
@@ -98,44 +98,48 @@ if [ -f "$PROJECT_DIR/artisan" ]; then
98
98
  echo "Cleared caches"
99
99
  fi
100
100
 
101
- # Remove composer integration if it was used
102
- if [ "$COMPOSER_USED" = true ]; then
103
- echo "Cleaning up composer integration..."
104
- php -r '
105
- $file = "composer.json";
106
- if (file_exists($file)) {
107
- $json = json_decode(file_get_contents($file), true);
108
- $changed = false;
109
-
110
- if (isset($json["require"]["lwazi/core"])) {
111
- unset($json["require"]["lwazi/core"]);
101
+ # Always clean up composer.json (lwazi might have added it even if mode wasn't saved)
102
+ echo "Cleaning up composer.json..."
103
+ php -r '
104
+ $file = "composer.json";
105
+ if (file_exists($file)) {
106
+ $json = json_decode(file_get_contents($file), true);
107
+ $changed = false;
108
+
109
+ if (isset($json["require"]["lwazi/core"])) {
110
+ unset($json["require"]["lwazi/core"]);
111
+ $changed = true;
112
+ }
113
+
114
+ if (isset($json["repositories"]) && is_array($json["repositories"])) {
115
+ $originalCount = count($json["repositories"]);
116
+ $json["repositories"] = array_filter($json["repositories"], function($repo) {
117
+ return !isset($repo["url"]) || $repo["url"] !== "lwazi";
118
+ });
119
+ if (count($json["repositories"]) !== $originalCount) {
112
120
  $changed = true;
113
121
  }
114
-
115
- if (isset($json["repositories"])) {
116
- $original = $json["repositories"];
117
- $json["repositories"] = array_filter($json["repositories"], function($repo) {
118
- return !isset($repo["url"]) || $repo["url"] !== "lwazi";
119
- });
120
- if (count($json["repositories"]) !== count($original)) {
121
- $changed = true;
122
- }
123
- if (empty($json["repositories"])) {
124
- unset($json["repositories"]);
125
- }
126
- }
127
-
128
- if ($changed) {
129
- file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
130
- echo "Removed lwazi from composer.json\n";
122
+ if (empty($json["repositories"])) {
123
+ unset($json["repositories"]);
131
124
  }
132
125
  }
133
- '
134
126
 
135
- # Run composer update to remove package
136
- if command -v composer &> /dev/null; then
137
- composer dump-autoload 2>/dev/null
138
- fi
127
+ // Remove minimum-stability if it was added for lwazi
128
+ if (isset($json["minimum-stability"]) && isset($json["prefer-stable"])) {
129
+ // Keep them if other packages need them
130
+ }
131
+
132
+ if ($changed) {
133
+ file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
134
+ echo "Removed lwazi from composer.json\n";
135
+ }
136
+ }
137
+ '
138
+
139
+ # Remove storage/lwazi directory
140
+ if [ -d "$PROJECT_DIR/storage/lwazi" ]; then
141
+ rm -rf "$PROJECT_DIR/storage/lwazi"
142
+ echo "Removed storage/lwazi directory"
139
143
  fi
140
144
 
141
145
  echo ""