@pippit-dev/cli 0.0.25 → 0.0.27

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/checksums.txt CHANGED
@@ -1,6 +1,6 @@
1
- b962c9bb2ce808ab57ed4cb7ea08f35f7ac407e92d1bab0c60c124e333df3882 pippit-tool-cli-0.0.25-darwin-amd64.tar.gz
2
- f99080aa1a7e5efbb46f86f21b677ca954e542ecd6722198d0f371d1d901410e pippit-tool-cli-0.0.25-darwin-arm64.tar.gz
3
- ae8f76ed27eb3d9af19d083526d8da53ed43a515e61eb497932af6eef77e91b6 pippit-tool-cli-0.0.25-linux-amd64.tar.gz
4
- 621b466278ca3be564cf996cfc884e4e1976f9e19227c2fa99c0a364a19c7087 pippit-tool-cli-0.0.25-linux-arm64.tar.gz
5
- 1d44b6a9a3f6f785d00e65693038119203d21b391c77733d84f6172846713252 pippit-tool-cli-0.0.25-windows-amd64.zip
6
- e978f14a65d6da4714fa9589c2e616ef42c68bc99031156bc86c1071606c244d pippit-tool-cli-0.0.25-windows-arm64.zip
1
+ 3e95e8bbd667579fa1feddd4c58e583d70003f0f179d7249135b883a612165e5 pippit-tool-cli-0.0.27-darwin-amd64.tar.gz
2
+ 15671a5880feff8a67bd0c8042ab7e2a77d1ea7c81ed6625427bdd472d925c6a pippit-tool-cli-0.0.27-darwin-arm64.tar.gz
3
+ aef2fc4896d2c8966f681a82e5bd624b9ec9f0382e23dd05776f014cbb65473d pippit-tool-cli-0.0.27-linux-amd64.tar.gz
4
+ 5b01a760298c22a6fd42661f212f9f1d1e822e84d1feb87307c36590f795515a pippit-tool-cli-0.0.27-linux-arm64.tar.gz
5
+ 18315d247bb409f594d11e64cb2292ffbf48504d73290011f5c5a2ba319f93df pippit-tool-cli-0.0.27-windows-amd64.zip
6
+ 31284208bc244d9d48066c8e7086bf40e167209fff106ea6462c9638cbd6efef pippit-tool-cli-0.0.27-windows-arm64.zip
@@ -1,24 +1,42 @@
1
1
  package updatecmd
2
2
 
3
3
  import (
4
+ "bytes"
5
+ "encoding/json"
4
6
  "fmt"
5
7
  "io"
8
+ "net/http"
6
9
  "os"
7
10
  "os/exec"
8
11
  "path/filepath"
9
12
  "runtime"
10
13
  "strings"
14
+ "time"
11
15
 
16
+ "github.com/Pippit-dev/pippit-cli/internal/version"
12
17
  "github.com/spf13/cobra"
13
18
  )
14
19
 
15
20
  const defaultPackage = "@pippit-dev/cli"
16
21
 
22
+ const (
23
+ defaultTelemetryBaseURL = "https://xyq.jianying.com"
24
+ telemetryPath = "/api/biz/v1/skill/report_telemetry"
25
+ telemetryAuthHeader = "Bearer pippit-cli-skill-telemetry"
26
+ )
27
+
17
28
  var legacyGlobalSkills = []string{
18
29
  "pippit-short-drama-skill",
19
30
  "xyq-nest-skill",
20
31
  }
21
32
 
33
+ var telemetrySkillNames = []string{
34
+ "xyq-skill",
35
+ "xyq-short-drama-skill",
36
+ }
37
+
38
+ var telemetryHTTPClient = &http.Client{Timeout: 2 * time.Second}
39
+
22
40
  // NewCommand builds the update command.
23
41
  func NewCommand(stdout, stderr io.Writer) *cobra.Command {
24
42
  cmd := &cobra.Command{
@@ -57,6 +75,7 @@ func runUpdate(stdout, stderr io.Writer) error {
57
75
  return fmt.Errorf("update pippit-tool-cli skills: %w", err)
58
76
  }
59
77
 
78
+ reportBundledSkillTelemetry("update", "cli_update", stderr)
60
79
  fmt.Fprintln(stdout, "pippit-tool-cli and skills updated")
61
80
  return nil
62
81
  }
@@ -105,6 +124,83 @@ func cleanupLegacyGlobalSkills(globalSkillsDir string) error {
105
124
  return nil
106
125
  }
107
126
 
127
+ type telemetryPayload struct {
128
+ Event string `json:"event"`
129
+ SkillName string `json:"skill_name"`
130
+ Source string `json:"source"`
131
+ CliVersion string `json:"cli_version"`
132
+ Platform string `json:"platform"`
133
+ Arch string `json:"arch"`
134
+ }
135
+
136
+ func reportBundledSkillTelemetry(event string, source string, stderr io.Writer) {
137
+ if os.Getenv("PIPPIT_CLI_DISABLE_TELEMETRY") == "1" {
138
+ return
139
+ }
140
+ for _, skillName := range telemetrySkillNames {
141
+ payload := telemetryPayload{
142
+ Event: event,
143
+ SkillName: skillName,
144
+ Source: source,
145
+ CliVersion: telemetryCliVersion(),
146
+ Platform: runtime.GOOS,
147
+ Arch: runtime.GOARCH,
148
+ }
149
+ go func(payload telemetryPayload) {
150
+ if err := reportSkillTelemetry(payload); err != nil && os.Getenv("PIPPIT_CLI_DEBUG_TELEMETRY") == "1" {
151
+ fmt.Fprintf(stderr, "[pippit-tool-cli] telemetry failed: %v\n", err)
152
+ }
153
+ }(payload)
154
+ }
155
+ }
156
+
157
+ func reportSkillTelemetry(payload telemetryPayload) error {
158
+ body, err := json.Marshal(payload)
159
+ if err != nil {
160
+ return err
161
+ }
162
+
163
+ req, err := http.NewRequest(http.MethodPost, telemetryBaseURL()+telemetryPath, bytes.NewReader(body))
164
+ if err != nil {
165
+ return err
166
+ }
167
+ req.Header.Set("Content-Type", "application/json")
168
+ req.Header.Set("Authorization", telemetryAuthHeader)
169
+ req.Header.Set("x-use-ppe", "1")
170
+ req.Header.Set("x-tt-env", "ppe_harness_novel_v2")
171
+
172
+ resp, err := telemetryHTTPClient.Do(req)
173
+ if err != nil {
174
+ return err
175
+ }
176
+ defer resp.Body.Close()
177
+ _, _ = io.Copy(io.Discard, resp.Body)
178
+ if resp.StatusCode >= 400 {
179
+ return fmt.Errorf("telemetry returned HTTP %d", resp.StatusCode)
180
+ }
181
+ return nil
182
+ }
183
+
184
+ func telemetryBaseURL() string {
185
+ for _, key := range []string{"PIPPIT_CLI_TELEMETRY_BASE_URL", "XYQ_OPENAPI_BASE", "XYQ_BASE_URL"} {
186
+ if value := strings.TrimRight(strings.TrimSpace(os.Getenv(key)), "/"); value != "" {
187
+ return value
188
+ }
189
+ }
190
+ return defaultTelemetryBaseURL
191
+ }
192
+
193
+ func telemetryCliVersion() string {
194
+ return stripPrereleaseVersion(version.Current())
195
+ }
196
+
197
+ func stripPrereleaseVersion(value string) string {
198
+ if idx := strings.Index(value, "-"); idx >= 0 {
199
+ return value[:idx]
200
+ }
201
+ return value
202
+ }
203
+
108
204
  func runInherit(stderr io.Writer, name string, args ...string) error {
109
205
  return runInheritEnv(stderr, nil, name, args...)
110
206
  }
@@ -2,6 +2,9 @@ package updatecmd
2
2
 
3
3
  import (
4
4
  "bytes"
5
+ "encoding/json"
6
+ "net/http"
7
+ "net/http/httptest"
5
8
  "os"
6
9
  "path/filepath"
7
10
  "runtime"
@@ -70,3 +73,67 @@ func TestCleanupLegacyGlobalSkills(t *testing.T) {
70
73
  }
71
74
  }
72
75
  }
76
+
77
+ func TestReportSkillTelemetry(t *testing.T) {
78
+ var gotAuth string
79
+ var gotHeaders http.Header
80
+ var gotPayload telemetryPayload
81
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
82
+ if r.URL.Path != telemetryPath {
83
+ t.Fatalf("path = %q, want %q", r.URL.Path, telemetryPath)
84
+ }
85
+ gotAuth = r.Header.Get("Authorization")
86
+ gotHeaders = r.Header.Clone()
87
+ if err := json.NewDecoder(r.Body).Decode(&gotPayload); err != nil {
88
+ t.Fatalf("decode request body: %v", err)
89
+ }
90
+ _, _ = w.Write([]byte(`{"ret":"0","errmsg":""}`))
91
+ }))
92
+ defer server.Close()
93
+
94
+ t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL+"/")
95
+ err := reportSkillTelemetry(telemetryPayload{
96
+ Event: "update",
97
+ SkillName: "xyq-skill",
98
+ Source: "cli_update",
99
+ CliVersion: "0.0.26",
100
+ Platform: "darwin",
101
+ Arch: "arm64",
102
+ })
103
+ if err != nil {
104
+ t.Fatalf("reportSkillTelemetry() error = %v", err)
105
+ }
106
+ if gotAuth != telemetryAuthHeader {
107
+ t.Fatalf("Authorization = %q, want %q", gotAuth, telemetryAuthHeader)
108
+ }
109
+ if got := gotHeaders.Get("x-use-ppe"); got != "1" {
110
+ t.Fatalf("x-use-ppe = %q, want 1", got)
111
+ }
112
+ if got := gotHeaders.Get("x-tt-env"); got != "ppe_harness_novel_v2" {
113
+ t.Fatalf("x-tt-env = %q, want ppe_harness_novel_v2", got)
114
+ }
115
+ if gotPayload.Event != "update" || gotPayload.SkillName != "xyq-skill" || gotPayload.Source != "cli_update" {
116
+ t.Fatalf("payload = %#v", gotPayload)
117
+ }
118
+ }
119
+
120
+ func TestTelemetryBaseURL(t *testing.T) {
121
+ t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", " ")
122
+ t.Setenv("XYQ_OPENAPI_BASE", "https://example.com///")
123
+ if got := telemetryBaseURL(); got != "https://example.com" {
124
+ t.Fatalf("telemetryBaseURL() = %q, want https://example.com", got)
125
+ }
126
+ }
127
+
128
+ func TestStripPrereleaseVersion(t *testing.T) {
129
+ cases := map[string]string{
130
+ "0.0.27": "0.0.27",
131
+ "0.0.27-rc.1": "0.0.27",
132
+ "1.2.3-beta.4": "1.2.3",
133
+ }
134
+ for input, want := range cases {
135
+ if got := stripPrereleaseVersion(input); got != want {
136
+ t.Fatalf("stripPrereleaseVersion(%q) = %q, want %q", input, got, want)
137
+ }
138
+ }
139
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pippit-dev/cli",
3
- "version": "0.0.25",
3
+ "version": "0.0.27",
4
4
  "description": "Pippit CLI",
5
5
  "bin": {
6
6
  "pippit-tool-cli": "scripts/run.js"
@@ -35,6 +35,7 @@
35
35
  "scripts/platform.js",
36
36
  "scripts/run.js",
37
37
  "scripts/skills.js",
38
+ "scripts/telemetry.js",
38
39
  "scripts/version-check.js",
39
40
  "checksums.txt",
40
41
  "README.md",
@@ -4,6 +4,7 @@ const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { isWindows, run, runSilent } = require("./platform");
6
6
  const { DEFAULT_PKG, installGlobalPackageSkills } = require("./skills");
7
+ const { reportBundledSkillTelemetry } = require("./telemetry");
7
8
 
8
9
  const PKG = process.env.PIPPIT_CLI_INSTALL_PACKAGE || DEFAULT_PKG;
9
10
 
@@ -68,6 +69,7 @@ function main() {
68
69
  }
69
70
 
70
71
  console.log(`pippit-tool-cli is ready: ${bin}`);
72
+ reportBundledSkillTelemetry("install", "npx_install");
71
73
  console.log("Try: pippit-tool-cli short-drama +submit-run --message \"写一个短剧开头\"");
72
74
  }
73
75
 
@@ -6,6 +6,7 @@ const os = require("os");
6
6
  const path = require("path");
7
7
  const { isWindows, run } = require("./platform");
8
8
  const { cleanupLegacyGlobalSkills, installSkillsFromRoot } = require("./skills");
9
+ const { reportBundledSkillTelemetry } = require("./telemetry");
9
10
 
10
11
  const VERSION = require("../package.json").version.replace(/-.*$/, "");
11
12
  const REPO = "Pippit-dev/cli";
@@ -134,6 +135,7 @@ function install() {
134
135
 
135
136
  if (process.env.PIPPIT_CLI_SKIP_SKILLS !== "1") {
136
137
  installSkillsFromRoot(ROOT);
138
+ reportBundledSkillTelemetry("install", "npm_install");
137
139
  } else {
138
140
  cleanupLegacyGlobalSkills();
139
141
  }
@@ -0,0 +1,83 @@
1
+ const os = require("os");
2
+ const http = require("http");
3
+ const https = require("https");
4
+
5
+ const VERSION = require("../package.json").version.replace(/-.*$/, "");
6
+ const DEFAULT_BASE_URL = "https://xyq.jianying.com";
7
+ const REPORT_PATH = "/api/biz/v1/skill/report_telemetry";
8
+ const AUTH_HEADER = "Bearer pippit-cli-skill-telemetry";
9
+ const SKILL_NAMES = [
10
+ "xyq-skill",
11
+ "xyq-short-drama-skill",
12
+ ];
13
+
14
+ function telemetryBaseURL() {
15
+ for (const key of ["PIPPIT_CLI_TELEMETRY_BASE_URL", "XYQ_OPENAPI_BASE", "XYQ_BASE_URL"]) {
16
+ const value = (process.env[key] || "").trim().replace(/\/+$/, "");
17
+ if (value) return value;
18
+ }
19
+ return DEFAULT_BASE_URL;
20
+ }
21
+
22
+ function reportBundledSkillTelemetry(event, source) {
23
+ if (process.env.PIPPIT_CLI_DISABLE_TELEMETRY === "1") {
24
+ return;
25
+ }
26
+ for (const skillName of SKILL_NAMES) {
27
+ reportSkillTelemetry({
28
+ event,
29
+ skill_name: skillName,
30
+ source,
31
+ cli_version: VERSION,
32
+ platform: os.platform(),
33
+ arch: os.arch(),
34
+ });
35
+ }
36
+ }
37
+
38
+ function reportSkillTelemetry(payload) {
39
+ const body = JSON.stringify(payload);
40
+ const url = new URL(`${telemetryBaseURL()}${REPORT_PATH}`);
41
+ const client = url.protocol === "http:" ? http : https;
42
+
43
+ try {
44
+ const req = client.request(url, {
45
+ method: "POST",
46
+ timeout: 2000,
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ "Content-Length": Buffer.byteLength(body),
50
+ "Authorization": AUTH_HEADER,
51
+ "x-use-ppe": "1",
52
+ "x-tt-env": "ppe_harness_novel_v2",
53
+ },
54
+ }, (res) => {
55
+ res.resume();
56
+ if (process.env.PIPPIT_CLI_DEBUG_TELEMETRY === "1" && res.statusCode >= 400) {
57
+ console.warn(`[pippit-tool-cli] telemetry failed: HTTP ${res.statusCode}`);
58
+ }
59
+ });
60
+
61
+ req.on("timeout", () => {
62
+ req.destroy(new Error("telemetry request timeout"));
63
+ });
64
+ req.on("error", (err) => {
65
+ if (process.env.PIPPIT_CLI_DEBUG_TELEMETRY === "1") {
66
+ const msg = err && err.message ? err.message : String(err);
67
+ console.warn(`[pippit-tool-cli] telemetry failed: ${msg}`);
68
+ }
69
+ });
70
+ req.end(body);
71
+ } catch (err) {
72
+ if (process.env.PIPPIT_CLI_DEBUG_TELEMETRY === "1") {
73
+ const msg = err && err.message ? err.message : String(err);
74
+ console.warn(`[pippit-tool-cli] telemetry failed: ${msg}`);
75
+ }
76
+ }
77
+ }
78
+
79
+ module.exports = {
80
+ reportBundledSkillTelemetry,
81
+ reportSkillTelemetry,
82
+ telemetryBaseURL,
83
+ };
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env python3
2
2
  """查询会话进展:POST /api/biz/v1/skill/get_thread,返回消息列表"""
3
3
 
4
- from _common import extract_entries_from_run
5
4
  import argparse
6
5
  import json
7
6
  import sys
8
7
  import os
9
8
 
10
9
  sys.path.insert(0, os.path.dirname(__file__))
11
- from _common import get_thread
10
+ from xyq_common import extract_entries_from_run
11
+ from xyq_common import get_thread
12
12
 
13
13
 
14
14
  def main():
@@ -7,7 +7,7 @@ import sys
7
7
  import os
8
8
 
9
9
  sys.path.insert(0, os.path.dirname(__file__))
10
- from _common import submit_run
10
+ from xyq_common import submit_run
11
11
 
12
12
 
13
13
  def main():
@@ -11,7 +11,7 @@ import urllib.request
11
11
  import urllib.error
12
12
 
13
13
  sys.path.insert(0, os.path.dirname(__file__))
14
- from _common import XYQ_BASE, ACCESS_KEY, UPLOAD_FILE_PATH, parse_response
14
+ from xyq_common import XYQ_BASE, ACCESS_KEY, UPLOAD_FILE_PATH, parse_response
15
15
 
16
16
  # 允许的 MIME 类型前缀
17
17
  ALLOWED_PREFIXES = ("image/", "video/")