@pippit-dev/cli 1.0.38 → 1.0.41

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.
Files changed (45) hide show
  1. package/README.md +84 -1
  2. package/checksums.txt +6 -6
  3. package/cmd/auth/auth.go +4 -3
  4. package/cmd/marketing.go +5 -4
  5. package/cmd/marketing_test.go +58 -0
  6. package/cmd/root.go +40 -14
  7. package/cmd/root_test.go +63 -0
  8. package/cmd/source_test.go +2 -3
  9. package/cmd/submit_run_test.go +20 -81
  10. package/cmd/update/update.go +36 -10
  11. package/cmd/update/update_test.go +73 -3
  12. package/dist/checksums.txt +6 -6
  13. package/dist/xyq-canvas-command-runtime.cjs +14 -14
  14. package/dist/xyq-canvas-command-runtime.cjs.LEGAL.txt +0 -206
  15. package/dist/xyq-canvas-command-runtime.cjs.sha256 +2 -2
  16. package/internal/adapter/adapter.go +73 -0
  17. package/internal/adapter/ppe.go +36 -0
  18. package/internal/adaptercli/commands.go +27 -0
  19. package/internal/adaptercli/commands_test.go +41 -0
  20. package/internal/auth/manager.go +25 -13
  21. package/internal/canvas/apply.go +3 -0
  22. package/internal/canvas/media_identity.go +300 -0
  23. package/internal/canvas/media_identity_test.go +160 -0
  24. package/internal/canvas/upload.go +30 -1
  25. package/internal/commandnames/names.go +9 -0
  26. package/internal/common/adapter_test.go +74 -0
  27. package/internal/common/client.go +37 -12
  28. package/internal/config/config.go +41 -0
  29. package/internal/config/config_test.go +41 -1
  30. package/internal/generate_video/generate_video.go +1 -0
  31. package/internal/generate_video/generate_video_test.go +25 -3
  32. package/internal/models/models.go +6 -2
  33. package/internal/models/models_test.go +7 -0
  34. package/package.json +2 -2
  35. package/scripts/canvas-command.js +4 -2
  36. package/skills/short-drama/SKILL.md +7 -4
  37. package/skills/short-drama/commands/download-result.md +9 -3
  38. package/skills/short-drama/commands/get-thread.md +2 -0
  39. package/skills/short-drama/commands/list-thread-file.md +12 -6
  40. package/skills/short-drama/commands/submit-run.md +3 -1
  41. package/skills/short-drama/examples/create-and-deliver.md +6 -2
  42. package/skills/short-drama/examples/reference-and-continue.md +20 -1
  43. package/skills/short-drama/workflows/creation.md +39 -14
  44. package/skills/short-drama/workflows/poll-and-deliver.md +27 -9
  45. package/skills/xyq-nest-skill/commands/canvas-assets.md +9 -1
@@ -14,7 +14,9 @@ import (
14
14
  "sync"
15
15
  "time"
16
16
 
17
+ "github.com/Pippit-dev/pippit-cli/internal/adapter"
17
18
  "github.com/Pippit-dev/pippit-cli/internal/auth"
19
+ "github.com/Pippit-dev/pippit-cli/internal/commandnames"
18
20
  "github.com/Pippit-dev/pippit-cli/internal/version"
19
21
  "github.com/spf13/cobra"
20
22
  )
@@ -38,29 +40,53 @@ var telemetrySkillNames = []string{
38
40
  "xyq-short-drama-skill",
39
41
  }
40
42
 
41
- var telemetryHTTPClient = &http.Client{Timeout: telemetryWaitTimeout}
43
+ // Each update command instance owns its HTTP client so instances using different adapters do not affect each other.
44
+ type updater struct{ telemetryHTTPClient *http.Client }
45
+
46
+ func newUpdater(policy adapter.HTTPAdapter) *updater {
47
+ return &updater{telemetryHTTPClient: &http.Client{
48
+ Timeout: telemetryWaitTimeout,
49
+ Transport: adapter.HTTPOrDefault(policy).WrapTransport(nil),
50
+ }}
51
+ }
42
52
 
43
53
  // NewCommand builds the update command.
44
54
  func NewCommand(stdout, stderr io.Writer) *cobra.Command {
55
+ return NewCommandWithAdapter(stdout, stderr, adapter.Default{})
56
+ }
57
+
58
+ func NewCommandWithAdapter(stdout, stderr io.Writer, policy adapter.HTTPAdapter) *cobra.Command {
59
+ updater := newUpdater(policy)
45
60
  var source string
61
+ var channel string
46
62
  cmd := &cobra.Command{
47
- Use: "update",
63
+ Use: commandnames.Update,
48
64
  Short: "Update pippit-tool-cli and bundled skills",
49
65
  RunE: func(cmd *cobra.Command, _ []string) error {
50
66
  if !cmd.Flags().Changed("source") {
51
67
  source = os.Getenv("PIPPIT_CLI_SOURCE")
52
68
  }
53
- return runUpdate(stdout, stderr, source)
69
+ return updater.runUpdate(stdout, stderr, source, channel)
54
70
  },
55
71
  }
56
72
  cmd.Flags().StringVar(&source, "source", "", "optional real host identifier reported as host_platform; defaults to PIPPIT_CLI_SOURCE when unset")
73
+ cmd.Flags().StringVar(&channel, "channel", "", "channel for this update only: stable (npm latest) or canary; overrides PIPPIT_CLI_INSTALL_PACKAGE")
57
74
  return cmd
58
75
  }
59
76
 
60
- func runUpdate(stdout, stderr io.Writer, source string) error {
77
+ func (u *updater) runUpdate(stdout, stderr io.Writer, hostPlatform, channel string) error {
61
78
  pkg := os.Getenv("PIPPIT_CLI_INSTALL_PACKAGE")
62
- if pkg == "" {
79
+ switch channel {
80
+ case "":
81
+ if pkg == "" {
82
+ pkg = defaultPackage + "@latest"
83
+ }
84
+ case "stable":
63
85
  pkg = defaultPackage + "@latest"
86
+ case "canary":
87
+ pkg = defaultPackage + "@canary"
88
+ default:
89
+ return fmt.Errorf("不支持的更新渠道 %q,可选值为 stable 或 canary", channel)
64
90
  }
65
91
 
66
92
  fmt.Fprintf(stderr, "Updating pippit-tool-cli via npm: %s\n", pkg)
@@ -83,7 +109,7 @@ func runUpdate(stdout, stderr io.Writer, source string) error {
83
109
  return fmt.Errorf("更新 pippit-tool-cli skills 失败: %w", err)
84
110
  }
85
111
 
86
- reportBundledSkillTelemetry("update", "cli_update", source, stderr)
112
+ u.reportBundledSkillTelemetry("update", "cli_update", hostPlatform, stderr)
87
113
  fmt.Fprintln(stdout, "pippit-tool-cli and skills updated")
88
114
  return nil
89
115
  }
@@ -143,7 +169,7 @@ type telemetryPayload struct {
143
169
  Arch string `json:"arch"`
144
170
  }
145
171
 
146
- func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.Writer) {
172
+ func (u *updater) reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.Writer) {
147
173
  if os.Getenv("PIPPIT_CLI_DISABLE_TELEMETRY") == "1" {
148
174
  return
149
175
  }
@@ -161,7 +187,7 @@ func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.W
161
187
  wg.Add(1)
162
188
  go func(payload telemetryPayload) {
163
189
  defer wg.Done()
164
- if err := reportSkillTelemetry(payload); err != nil && os.Getenv("PIPPIT_CLI_DEBUG_TELEMETRY") == "1" {
190
+ if err := u.reportSkillTelemetry(payload); err != nil && os.Getenv("PIPPIT_CLI_DEBUG_TELEMETRY") == "1" {
165
191
  fmt.Fprintf(stderr, "[pippit-tool-cli] 埋点上报失败: %v\n", err)
166
192
  }
167
193
  }(payload)
@@ -177,7 +203,7 @@ func reportBundledSkillTelemetry(event, source, hostPlatform string, stderr io.W
177
203
  }
178
204
  }
179
205
 
180
- func reportSkillTelemetry(payload telemetryPayload) error {
206
+ func (u *updater) reportSkillTelemetry(payload telemetryPayload) error {
181
207
  body, err := json.Marshal(payload)
182
208
  if err != nil {
183
209
  return err
@@ -190,7 +216,7 @@ func reportSkillTelemetry(payload telemetryPayload) error {
190
216
  req.Header.Set("Content-Type", "application/json")
191
217
  req.Header.Set("Authorization", telemetryAuthHeader)
192
218
 
193
- resp, err := telemetryHTTPClient.Do(req)
219
+ resp, err := u.telemetryHTTPClient.Do(req)
194
220
  if err != nil {
195
221
  return err
196
222
  }
@@ -11,8 +11,78 @@ import (
11
11
  "strings"
12
12
  "testing"
13
13
  "time"
14
+
15
+ "github.com/Pippit-dev/pippit-cli/internal/adapter"
14
16
  )
15
17
 
18
+ func TestUpdateCommandSelectsChannel(t *testing.T) {
19
+ if runtime.GOOS == "windows" {
20
+ t.Skip("test uses POSIX shell scripts to stub npm and npx")
21
+ }
22
+ cases := []struct {
23
+ name string
24
+ args []string
25
+ override string
26
+ want string
27
+ wantErr bool
28
+ }{
29
+ {name: "default stable", want: defaultPackage + "@latest"},
30
+ {name: "explicit stable", args: []string{"--channel", "stable"}, want: defaultPackage + "@latest"},
31
+ {name: "explicit canary", args: []string{"--channel", "canary"}, want: defaultPackage + "@canary"},
32
+ {name: "package override preserved", override: defaultPackage + "@1.0.38", want: defaultPackage + "@1.0.38"},
33
+ {name: "stable overrides environment", args: []string{"--channel", "stable"}, override: defaultPackage + "@canary", want: defaultPackage + "@latest"},
34
+ {name: "canary overrides environment", args: []string{"--channel", "canary"}, override: defaultPackage + "@1.0.38", want: defaultPackage + "@canary"},
35
+ {name: "invalid channel", args: []string{"--channel", "beta"}, wantErr: true},
36
+ {name: "invalid channel with override", args: []string{"--channel", "beta"}, override: defaultPackage + "@1.0.38", wantErr: true},
37
+ }
38
+ for _, tc := range cases {
39
+ t.Run(tc.name, func(t *testing.T) {
40
+ binDir := t.TempDir()
41
+ npmRoot := t.TempDir()
42
+ capturePath := filepath.Join(t.TempDir(), "npm-args.txt")
43
+ if err := os.MkdirAll(filepath.Join(npmRoot, "@pippit-dev", "cli", "skills"), 0o755); err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ npmScript := "#!/bin/sh\ncase \"$1\" in\ninstall) printf '%s\\n' \"$@\" > \"$CAPTURE_ARGS\";;\nroot) printf '%s\\n' \"$TEST_NPM_ROOT\";;\n*) exit 1;;\nesac\n"
47
+ for name, script := range map[string]string{"npm": npmScript, "npx": "#!/bin/sh\nexit 0\n"} {
48
+ if err := os.WriteFile(filepath.Join(binDir, name), []byte(script), 0o755); err != nil {
49
+ t.Fatal(err)
50
+ }
51
+ }
52
+ t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
53
+ t.Setenv("HOME", t.TempDir())
54
+ t.Setenv("CAPTURE_ARGS", capturePath)
55
+ t.Setenv("TEST_NPM_ROOT", npmRoot)
56
+ t.Setenv("PIPPIT_CLI_DISABLE_TELEMETRY", "1")
57
+ t.Setenv("PIPPIT_CLI_INSTALL_PACKAGE", tc.override)
58
+ var stdout, stderr bytes.Buffer
59
+ cmd := NewCommand(&stdout, &stderr)
60
+ cmd.SetArgs(append([]string{}, tc.args...))
61
+ err := cmd.Execute()
62
+ if tc.wantErr {
63
+ if err == nil || !strings.Contains(err.Error(), "不支持的更新渠道") {
64
+ t.Fatalf("error = %v, want invalid channel error", err)
65
+ }
66
+ if _, err := os.Stat(capturePath); !os.IsNotExist(err) {
67
+ t.Fatalf("invalid channel invoked npm or stat failed: %v", err)
68
+ }
69
+ return
70
+ }
71
+ if err != nil {
72
+ t.Fatalf("update failed: %v, stderr: %s", err, stderr.String())
73
+ }
74
+ got, err := os.ReadFile(capturePath)
75
+ if err != nil {
76
+ t.Fatal(err)
77
+ }
78
+ want := strings.Join([]string{"install", "-g", "--prefer-online", "--foreground-scripts", tc.want, ""}, "\n")
79
+ if string(got) != want {
80
+ t.Fatalf("npm args = %q, want %q", got, want)
81
+ }
82
+ })
83
+ }
84
+ }
85
+
16
86
  func TestInstallSkillsInstallsAllBundledSkills(t *testing.T) {
17
87
  if runtime.GOOS == "windows" {
18
88
  t.Skip("test uses a POSIX shell script to stub npx")
@@ -92,7 +162,7 @@ func TestReportSkillTelemetry(t *testing.T) {
92
162
  defer server.Close()
93
163
 
94
164
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL+"/")
95
- err := reportSkillTelemetry(telemetryPayload{
165
+ err := newUpdater(adapter.Default{}).reportSkillTelemetry(telemetryPayload{
96
166
  Event: "update",
97
167
  SkillName: "xyq-skill",
98
168
  Source: "cli_update",
@@ -121,7 +191,7 @@ func TestReportBundledSkillTelemetryWaitsBriefly(t *testing.T) {
121
191
 
122
192
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL)
123
193
  start := time.Now()
124
- reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
194
+ newUpdater(adapter.Default{}).reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
125
195
  if elapsed := time.Since(start); elapsed > 1500*time.Millisecond {
126
196
  t.Fatalf("reportBundledSkillTelemetry() blocked for %v, want <= 1.5s", elapsed)
127
197
  }
@@ -143,7 +213,7 @@ func TestReportBundledSkillTelemetryReportsBothSkills(t *testing.T) {
143
213
  defer server.Close()
144
214
 
145
215
  t.Setenv("PIPPIT_CLI_TELEMETRY_BASE_URL", server.URL)
146
- reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
216
+ newUpdater(adapter.Default{}).reportBundledSkillTelemetry("update", "cli_update", " workbuddy ", &bytes.Buffer{})
147
217
 
148
218
  got := map[string]bool{}
149
219
  for i := 0; i < len(telemetrySkillNames); i++ {
@@ -1,6 +1,6 @@
1
- 3fca50bc9ffb2c14e74ea65781fb706d4fa2c79484d5b20b3c08ad15847afc30 pippit-tool-cli-1.0.38-darwin-amd64.tar.gz
2
- 0e7d976179d944a2b8f99800acb91c6d3c035f0920cf4ec4348010b3a03d9e38 pippit-tool-cli-1.0.38-darwin-arm64.tar.gz
3
- 4f5723713fc910121982885172079ab1e787d2af9f2515961e59f7e53ba48737 pippit-tool-cli-1.0.38-linux-amd64.tar.gz
4
- 07d8ff5920dfa6748ad46076fcffbabc7b5d8dcef6714d802c7fa5b4b515494e pippit-tool-cli-1.0.38-linux-arm64.tar.gz
5
- 61f18a00f457ffff80c20593b3c0af84b93a3e8db871b00ed043f61024c6b441 pippit-tool-cli-1.0.38-windows-amd64.zip
6
- c07399e11956f7c6565e00c74a24f864012613539a61341636f9900d85a72654 pippit-tool-cli-1.0.38-windows-arm64.zip
1
+ 6f7daba79b3423b148e30373bb87e7e8998df3d76be2196e39f84af209f0ea50 pippit-tool-cli-1.0.41-darwin-amd64.tar.gz
2
+ 9f03ae01c213a76f45b0dfa110272b45e5b5ecd54b188313c9d256ccbe512980 pippit-tool-cli-1.0.41-darwin-arm64.tar.gz
3
+ ea603412d1c5b0baa42efd34bec92fcd0907333babd64a6e08514d4e2dcb86e5 pippit-tool-cli-1.0.41-linux-amd64.tar.gz
4
+ cebd1f0f34157624c0fc8203eec306bd6c7e31a2db44089f52bca9c74c57ee03 pippit-tool-cli-1.0.41-linux-arm64.tar.gz
5
+ 729f413373d185964a42c178a7e0eeafc3fca490670fb753dc36fb38bb47aaf7 pippit-tool-cli-1.0.41-windows-amd64.zip
6
+ b49491ae751e72f994da9388777a43f1b6b5781399dc2da15ced6d74f48dad6d pippit-tool-cli-1.0.41-windows-arm64.zip