Просмотр исходного кода

impl 005-logseq-cli-output-and-db-worker-node-log.md (2)

rcmerci 1 день назад
Родитель
Сommit
5dd4ebdc8a

+ 1 - 1
docs/agent-guide/005-logseq-cli-output-and-db-worker-node-log.md

@@ -20,7 +20,7 @@ Target: plain text, no ANSI colors. Each command has a stable layout and orderin
 | graph switch | `Graph switched: <graph>` | n/a | Use graph name from action/options |
 | graph remove | `Graph removed: <graph>` | n/a | Use graph name from action/options |
 | graph validate | `Graph validated: <graph>` | n/a | Use graph name from action/options |
-| graph info | Lines: `Graph: <graph>`, `Created at: <ts>`, `Schema version: <v>` | n/a | Use `:logseq.kv/*` data; show `-` if missing |
+| graph info | Lines: `Graph: <graph>`, `Created at: <relative time>`, `Schema version: <v>` | n/a | Use `:logseq.kv/*` data; show `-` if missing; `Created at` should use the same human-friendly relative format as list outputs |
 | server list | Table with header `REPO STATUS HOST PORT PID`, rows for servers, followed by `Count: N` | Header + `Count: 0` | Data from `{:servers [...]}` |
 | server status/start/stop/restart | `Server <status>: <repo>` + details line `Host: <host>  Port: <port>` when available | n/a | Use `:status` keyword where present |
 | list page/tag/property | Table with header (fields vary by command) and rows, followed by `Count: N` | Header + `Count: 0` | Defaults: page/tag/property `ID TITLE UPDATED-AT CREATED-AT` (ID uses `:db/id`); if `:db/ident` present, include `IDENT` column |

+ 12 - 3
docs/cli/logseq-cli.md

@@ -1,6 +1,6 @@
 # Logseq CLI (Node)
 
-The Logseq CLI is a Node.js program compiled from ClojureScript that connects to a db-worker-node server managed by the CLI.
+The Logseq CLI is a Node.js program compiled from ClojureScript that connects to a db-worker-node server managed by the CLI. When installed, the CLI binary name is `logseq`.
 
 ## Build the CLI
 
@@ -10,12 +10,18 @@ clojure -M:cljs compile logseq-cli
 
 ## db-worker-node lifecycle
 
-`logseq-cli` manages `db-worker-node` automatically. You should not start the server manually. The server binds to localhost on a random port and records that port in the repo lock file.
+`logseq` manages `db-worker-node` automatically. You should not start the server manually. The server binds to localhost on a random port and records that port in the repo lock file.
 
 ## Run the CLI
 
 ```bash
 node ./static/logseq-cli.js graph list
+
+If installed globally, run:
+
+```bash
+logseq graph list
+```
 ```
 
 ## Configuration
@@ -81,9 +87,12 @@ Subcommands:
   show [options]           Show tree
 ```
 
+Options grouping:
+- Help output separates **Global options** (apply to all commands) and **Command options** (command-specific flags).
+
 Output formats:
 - Global `--output <human|json|edn>` (also accepted per subcommand)
-- Human output is plain text. List/search commands render tables with a final `Count: N` line. For list subcommands, the ID column uses `:db/id` (not UUID). If `:db/ident` exists, an `IDENT` column is included. Errors include error codes and may include a `Hint:` line. Use `--output json|edn` for structured output.
+- Human output is plain text. List/search commands render tables with a final `Count: N` line. For list subcommands, the ID column uses `:db/id` (not UUID). If `:db/ident` exists, an `IDENT` column is included. Times such as list `UPDATED-AT`/`CREATED-AT` and `graph info` `Created at` are shown in human-friendly relative form. Errors include error codes and may include a `Hint:` line. Use `--output json|edn` for structured output.
 
 Examples:
 

+ 44 - 20
src/main/logseq/cli/commands.cljs

@@ -131,13 +131,16 @@
   [group table]
   (let [group-table (filter #(= group (first (:cmds %))) table)]
     (string/join "\n"
-                 [(str "Usage: logseq-cli " group " <subcommand> [options]")
+                 [(str "Usage: logseq " group " <subcommand> [options]")
                   ""
                   "Subcommands:"
                   (format-commands group-table)
                   ""
-                  "Options:"
-                  (cli/format-opts {:spec global-spec})])))
+                  "Global options:"
+                  (cli/format-opts {:spec global-spec})
+                  ""
+                  "Command options:"
+                  (str "  See `logseq " group " <subcommand> --help`")])))
 
 (defn- top-level-summary
   [table]
@@ -149,21 +152,28 @@
                        (let [entries (filter #(contains? commands (first (:cmds %))) table)]
                          (string/join "\n" [title (format-commands entries)])))]
     (string/join "\n"
-                 ["Usage: logseq-cli <command> [options]"
+                 ["Usage: logseq <command> [options]"
                   ""
                   "Commands:"
                   (string/join "\n\n" (map render-group groups))
                   ""
-                  "Options:"
-                  (cli/format-opts {:spec global-spec})])))
+                  "Global options:"
+                  (cli/format-opts {:spec global-spec})
+                  ""
+                  "Command options:"
+                  "  See `logseq <command> --help`"])))
 
 (defn- command-summary
   [{:keys [cmds spec]}]
-  (string/join "\n"
-               [(str "Usage: logseq-cli " (string/join " " cmds) " [options]")
-                ""
-                "Options:"
-                (cli/format-opts {:spec spec})]))
+  (let [command-spec (apply dissoc spec (keys global-spec))]
+    (string/join "\n"
+                 [(str "Usage: logseq " (string/join " " cmds) " [options]")
+                  ""
+                  "Global options:"
+                  (cli/format-opts {:spec global-spec})
+                  ""
+                  "Command options:"
+                  (cli/format-opts {:spec command-spec})])))
 
 (defn- merge-spec
   [spec]
@@ -631,29 +641,43 @@
     (build root-id 1)))
 
 (defn- fetch-tree
-  [config {:keys [repo id uuid page-name level]}]
-  (let [max-depth (or level 10)]
+  [config {:keys [repo id page-name level] :as opts}]
+  (let [max-depth (or level 10)
+        uuid-str (:uuid opts)]
     (cond
       (some? id)
       (p/let [entity (transport/invoke config "thread-api/pull" false
-                                       [repo [:db/id :block/uuid :block/title {:block/page [:db/id :block/title]}] id])]
+                                       [repo [:db/id :block/name :block/uuid :block/title {:block/page [:db/id :block/title]}] id])]
         (if-let [page-id (get-in entity [:block/page :db/id])]
           (p/let [blocks (fetch-blocks-for-page config repo page-id)
                   children (build-tree blocks (:db/id entity) max-depth)]
             {:root (assoc entity :block/children children)})
-          (throw (ex-info "block not found" {:code :block-not-found}))))
+          (if (:db/id entity)
+            (p/let [blocks (fetch-blocks-for-page config repo (:db/id entity))
+                    children (build-tree blocks (:db/id entity) max-depth)]
+              {:root (assoc entity :block/children children)})
+            (throw (ex-info "block not found" {:code :block-not-found})))))
 
-      (seq uuid)
-      (if-not (common-util/uuid-string? uuid)
+      (seq uuid-str)
+      (if-not (common-util/uuid-string? uuid-str)
         (p/rejected (ex-info "block must be a uuid" {:code :invalid-block}))
         (p/let [entity (transport/invoke config "thread-api/pull" false
-                                         [repo [:db/id :block/uuid :block/title {:block/page [:db/id :block/title]}]
-                                          [:block/uuid (uuid uuid)]])]
+                                         [repo [:db/id :block/name :block/uuid :block/title {:block/page [:db/id :block/title]}]
+                                          [:block/uuid (uuid uuid-str)]])
+                entity (if (:db/id entity)
+                         entity
+                         (transport/invoke config "thread-api/pull" false
+                                           [repo [:db/id :block/name :block/uuid :block/title {:block/page [:db/id :block/title]}]
+                                            [:block/uuid uuid-str]]))]
           (if-let [page-id (get-in entity [:block/page :db/id])]
             (p/let [blocks (fetch-blocks-for-page config repo page-id)
                     children (build-tree blocks (:db/id entity) max-depth)]
               {:root (assoc entity :block/children children)})
-            (throw (ex-info "block not found" {:code :block-not-found})))))
+            (if (:db/id entity)
+              (p/let [blocks (fetch-blocks-for-page config repo (:db/id entity))
+                      children (build-tree blocks (:db/id entity) max-depth)]
+                {:root (assoc entity :block/children children)})
+              (throw (ex-info "block not found" {:code :block-not-found}))))))
 
       (seq page-name)
       (p/let [page-entity (transport/invoke config "thread-api/pull" false

+ 6 - 4
src/main/logseq/cli/format.cljs

@@ -176,10 +176,12 @@
          (or results []))))
 
 (defn- format-graph-info
-  [{:keys [graph logseq.kv/graph-created-at logseq.kv/schema-version]}]
+  [{:keys [graph logseq.kv/graph-created-at logseq.kv/schema-version]} now-ms]
   (string/join "\n"
                [(str "Graph: " (or graph "-"))
-                (str "Created at: " (or graph-created-at "-"))
+                (str "Created at: " (if (some? graph-created-at)
+                                      (human-ago graph-created-at now-ms)
+                                      "-"))
                 (str "Schema version: " (or schema-version "-"))]))
 
 (defn- format-server-status
@@ -233,7 +235,7 @@
       :ok
       (case command
         :graph-list (format-graph-list (:graphs data))
-        :graph-info (format-graph-info data)
+        :graph-info (format-graph-info data now-ms)
         (:graph-create :graph-switch :graph-remove :graph-validate)
         (format-graph-action command context)
         :server-list (format-server-list (:servers data))
@@ -264,7 +266,7 @@
             (= status :error) (assoc :error error))))
 
 (defn format-result
-  [result {:keys [output-format now-ms] :as opts}]
+  [result {:keys [output-format] :as opts}]
   (let [format (cond
                  (= output-format :edn) :edn
                  (= output-format :json) :json

+ 1 - 1
src/main/logseq/cli/main.cljs

@@ -10,7 +10,7 @@
 (defn- usage
   [summary]
   (string/join "\n"
-               ["logseq-cli <command> [options]"
+               ["logseq <command> [options]"
                 ""
                 "Commands: list page, list tag, list property, add block, add page, remove block, remove page, search, show, graph list, graph create, graph switch, graph remove, graph validate, graph info, server list, server status, server start, server stop, server restart"
                 ""

+ 1 - 1
src/main/logseq/cli/server.cljs

@@ -1,5 +1,5 @@
 (ns logseq.cli.server
-  "db-worker-node lifecycle orchestration for logseq-cli."
+  "db-worker-node lifecycle orchestration for logseq."
   (:require ["child_process" :as child-process]
             ["fs" :as fs]
             ["http" :as http]

+ 16 - 6
src/test/logseq/cli/commands_test.cljs

@@ -18,9 +18,14 @@
       (is (string/includes? summary "search"))
       (is (string/includes? summary "show"))
       (is (string/includes? summary "graph"))
-      (is (string/includes? summary "server")))))
+      (is (string/includes? summary "server"))))
 
-(deftest test-parse-args
+  (testing "top-level help separates global and command options"
+    (let [summary (:summary (commands/parse-args ["--help"]))]
+      (is (string/includes? summary "Global options:"))
+      (is (string/includes? summary "Command options:")))))
+
+(deftest test-parse-args-help
   (testing "graph group shows subcommands"
     (let [result (commands/parse-args ["graph"])
           summary (:summary result)]
@@ -34,7 +39,9 @@
       (is (true? (:help? result)))
       (is (string/includes? summary "list page"))
       (is (string/includes? summary "list tag"))
-      (is (string/includes? summary "list property"))))
+      (is (string/includes? summary "list property"))
+      (is (string/includes? summary "Global options:"))
+      (is (string/includes? summary "Command options:"))))
 
   (testing "add group shows subcommands"
     (let [result (commands/parse-args ["add"])
@@ -55,8 +62,9 @@
           summary (:summary result)]
       (is (true? (:help? result)))
       (is (string/includes? summary "server list"))
-      (is (string/includes? summary "server start"))))
+      (is (string/includes? summary "server start")))))
 
+(deftest test-parse-args-help-alignment
   (testing "graph group aligns subcommand columns"
     (let [result (commands/parse-args ["graph"])
           summary (:summary result)
@@ -85,8 +93,9 @@
                                    (when-let [[_ desc] (re-matches #"^\s+.*?\s{2,}(.+)$" line)]
                                      (.indexOf line desc)))))]
       (is (seq subcommand-lines))
-      (is (apply = desc-starts))))
+      (is (apply = desc-starts)))))
 
+(deftest test-parse-args-errors
   (testing "rejects legacy commands"
     (doseq [command ["graph-list" "graph-create" "graph-switch" "graph-remove"
                      "graph-validate" "graph-info" "block" "tree"
@@ -113,8 +122,9 @@
   (testing "errors on unknown command"
     (let [result (commands/parse-args ["wat"])]
       (is (false? (:ok? result)))
-      (is (= :unknown-command (get-in result [:error :code])))))
+      (is (= :unknown-command (get-in result [:error :code]))))))
 
+(deftest test-parse-args-global-options
   (testing "global output option is accepted"
     (let [result (commands/parse-args ["--output" "json" "graph" "list"])]
       (is (true? (:ok? result)))

+ 4 - 3
src/test/logseq/cli/format_test.cljs

@@ -110,11 +110,12 @@
     (let [result (format/format-result {:status :ok
                                         :command :graph-info
                                         :data {:graph "demo-graph"
-                                               :logseq.kv/graph-created-at 123
+                                               :logseq.kv/graph-created-at 40000
                                                :logseq.kv/schema-version 2}}
-                                       {:output-format nil})]
+                                       {:output-format nil
+                                        :now-ms 100000})]
       (is (= (str "Graph: demo-graph\n"
-                  "Created at: 123\n"
+                  "Created at: 1m ago\n"
                   "Schema version: 2")
              result)))))
 

+ 37 - 0
src/test/logseq/cli/integration_test.cljs

@@ -173,3 +173,40 @@
           (p/catch (fn [e]
                      (is false (str "unexpected error: " e))
                      (done)))))))
+
+(deftest test-cli-show-page-block-by-id-and-uuid
+  (async done
+    (let [data-dir (node-helper/create-tmp-dir "db-worker")]
+      (-> (p/let [cfg-path (node-path/join (node-helper/create-tmp-dir "cli") "cli.edn")
+                  _ (fs/writeFileSync cfg-path "{:output-format :json}")
+                  _ (run-cli ["graph" "create" "--repo" "show-page-block-graph"] data-dir cfg-path)
+                  _ (run-cli ["add" "page" "--page" "TestPage"] data-dir cfg-path)
+                  list-page-result (run-cli ["list" "page" "--expand"] data-dir cfg-path)
+                  list-page-payload (parse-json-output list-page-result)
+                  page-item (some (fn [item]
+                                    (when (= "TestPage" (or (:block/title item) (:title item)))
+                                      item))
+                                  (get-in list-page-payload [:data :items]))
+                  page-id (or (:db/id page-item) (:id page-item))
+                  page-uuid (or (:block/uuid page-item) (:uuid page-item))
+                  show-by-id-result (run-cli ["show" "--id" (str page-id) "--format" "json"] data-dir cfg-path)
+                  show-by-id-payload (parse-json-output show-by-id-result)
+                  show-by-uuid-result (run-cli ["show" "--uuid" (str page-uuid) "--format" "json"] data-dir cfg-path)
+                  show-by-uuid-payload (parse-json-output show-by-uuid-result)
+                  stop-result (run-cli ["server" "stop" "--repo" "show-page-block-graph"] data-dir cfg-path)
+                  stop-payload (parse-json-output stop-result)]
+            (is (= "ok" (:status list-page-payload)))
+            (is (some? page-item))
+            (is (some? page-id))
+            (is (some? page-uuid))
+            (is (= "ok" (:status show-by-id-payload)))
+            (is (= (str page-uuid) (str (or (get-in show-by-id-payload [:data :root :uuid])
+                                            (get-in show-by-id-payload [:data :root :block/uuid])))))
+            (is (= "ok" (:status show-by-uuid-payload)))
+            (is (= (str page-uuid) (str (or (get-in show-by-uuid-payload [:data :root :uuid])
+                                            (get-in show-by-uuid-payload [:data :root :block/uuid])))))
+            (is (= "ok" (:status stop-payload)))
+            (done))
+          (p/catch (fn [e]
+                     (is false (str "unexpected error: " e))
+                     (done)))))))