Running Wiki from Glamorous Toolkit

Short answers first: > **Can we attach to an already running process?** Not in the same way that `GtExternalProcessBuilder` does for processes it spawns. GT’s external-process support assumes it created the process (so it owns the pipes for stdin/stdout/stderr, lifetime, etc.). For an *already running* OS process (e.g. a `wiki` you started in a terminal), GT can: * talk to it via its **network API** (HTTP, etc.), or * inspect it indirectly via `ps`, `lsof`, etc. using its PID …but it cannot retroactively “attach” to its stdio streams or turn it into a `GtNativeExternalProcessChild` with live stdout. You *can* wrap a PID in your own model object and query `ps` to get state, but you won’t get its stdout/stderr history or direct control over its file descriptors. --- Now a refactored version of your page. --- ````markdown --- Title: npm install -g wiki / Welcome Visitors --- # npm install -g wiki / Welcome Visitors ## 1. Install and run wiki with npm Running your own Server ([github](https://github.com/fedwiki/wiki#running-your-own-server)): ```shell npm install -g wiki wiki ```` ```Shell script npm install -g wiki ``` **Note** `npm WARN deprecated formidable@1.2.6: Please upgrade to latest, formidable@v2 or formidable@v3!` See the [VERSION_NOTES](https://github.com/node-formidable/formidable/blob/master/VERSION_NOTES.md). Related: * Fixing npm issue in GT — start GT from the right env * {{gtPage:Wiki ./. MCP Server|db=bh7jlpjzxanuf3kzv4gpjp737}} * {{gtPage:Wiki|db=aglh1ce18ttiplvtwozjipfl5}} (Farm mode) * {{gtPage:How to work with external child processes|db=2j9m7db2i4oz116bexd7wbdxo}} --- ## 2. Starting `wiki` from GT The following snippet creates an indefinitely running process that runs `wiki` and pipes `stdout` into a buffer: ```Pharo process := GtExternalProcessBuilder new command: 'wiki'; pipeStdout; spawn ``` The actual OS child processes are instances of `GtNativeExternalProcessChild`: ```Pharo GtNativeExternalProcessChild allInstances ``` For `GtNativeExternalProcessChild`, `id` is the **OS PID** (the one you can use with `ps`). Example `ps` call (from a Shell script snippet in GT, bound to `psOutput`): ```Shell script ps -p 14569 -o pid,ppid,tty,stat,start,time,command ``` ```Pharo psOutput "string with the ps stdout" ``` See also [issue #4454](https://github.com/feenkcom/gtoolkit/issues/4454). --- ## 3. Parsing `ps` output in Pharo ### 3.1. Simple header → value dictionary ```Pharo | lines headers values row | lines := psOutput lines. "Split header and data line into tokens, drop empty ones" headers := (lines first findTokens: Character space) reject: #isEmpty. values := (lines second findTokens: Character space) reject: #isEmpty. "Headers -> values dictionary (COMMAND will be truncated if it has spaces)" row := Dictionary new. 1 to: headers size do: [ :i | row at: (headers at: i) asSymbol put: (values at: i) ]. row ``` ### 3.2. Keep full COMMAND (including path and args) ```Pharo | lines headerLine dataLine headers dataTokens firstN commandTokens command values row | "psOutput is the stdout from `ps -p ...`" lines := psOutput lines. headerLine := lines first. dataLine := lines second. "Column names (PID PPID TTY STAT STARTED TIME COMMAND)" headers := (headerLine findTokens: Character space) reject: #isEmpty. "All data tokens of the second line" dataTokens := (dataLine findTokens: Character space) reject: #isEmpty. "All columns except the last one" firstN := dataTokens first: headers size - 1. "Tokens that belong to COMMAND (may contain spaces)" commandTokens := dataTokens allButFirst: headers size - 1. "Join COMMAND tokens with spaces" command := String streamContents: [ :stream | commandTokens doWithIndex: [ :each :i | i > 1 ifTrue: [ stream space ]. stream nextPutAll: each ] ]. "Rebuild values list so it matches headers 1:1" values := firstN copyWith: command. "Headers -> values dictionary" row := Dictionary new. 1 to: headers size do: [ :i | row at: (headers at: i) asSymbol put: (values at: i) ]. row "row at: #COMMAND -> 'node /Users/rgb/.npm-global/bin/wiki'" ``` You can now correlate a `GtNativeExternalProcessChild`: ```Pharo wikiChild := GtNativeExternalProcessChild allInstances detect: [ :each | each id = (row at: #PID) asNumber ]. ``` --- ## 4. Polling `wiki` stdout into a transcript The piped `stdout` is buffered and can be polled at regular intervals. The code snippet below polls `stdout` as a string every `0.5 seconds` and prints it into a local transcript: ```Pharo transcript := GtTranscript new. reader := process asynchronousStdout pollStringEvery: 0.5 second do: [ :aString | transcript show: aString ]. transcript ``` Keep a reference if you want to inspect the transcript later: ```Pharo log := transcript ``` To explore transcripts: ```Pharo GtTranscript allInstances ``` N.B. This is a {{gtClass:GtTranscript}}. Question: What does {{gtMethod:GtTranscript class>>#streamContents:}} do? (We want to eventually stream this output to DMX.) --- ## 5. Open wiki in the browser ```Pharo WebBrowser openOn: 'http://localhost:3000/view/welcome-visitors' ``` You can also navigate to {{gtPage:Sitemap|db=aglh1ce18ttiplvtwozjipfl5}} once the wiki is running. --- ## 6. Attaching to an already running process? > **NB. Could we attach to an already running process?** Currently, GT’s external process support does **not** attach to arbitrary existing OS processes. It only manages processes it spawns (via `GtExternalProcessBuilder`), where it owns the stdin/stdout/stderr pipes. For a process started elsewhere (e.g., `wiki` launched in a terminal): * You can **talk to it** over its network port (HTTP). * You can **inspect it** via `ps`, `lsof`, etc., using its PID. * But you **cannot retroactively capture** its stdout/stderr or turn it into a `GtNativeExternalProcessChild` with live streams. A possible extension would be a “PID wrapper” object that uses `ps` and friends, but that’s a different abstraction than GT’s current external-process model. ``` ```

–– chatgpt