@pippit-dev/cli 1.0.37 → 1.0.40

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 (48) hide show
  1. package/README.md +86 -3
  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/scripts/install-wizard.js +3 -3
  37. package/scripts/install.js +1 -1
  38. package/scripts/telemetry.js +16 -0
  39. package/skills/short-drama/SKILL.md +6 -3
  40. package/skills/short-drama/commands/download-result.md +9 -3
  41. package/skills/short-drama/commands/get-thread.md +2 -0
  42. package/skills/short-drama/commands/list-thread-file.md +12 -6
  43. package/skills/short-drama/commands/submit-run.md +3 -1
  44. package/skills/short-drama/examples/create-and-deliver.md +2 -2
  45. package/skills/short-drama/examples/reference-and-continue.md +20 -1
  46. package/skills/short-drama/workflows/creation.md +28 -14
  47. package/skills/short-drama/workflows/poll-and-deliver.md +27 -9
  48. 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
- ef54466711739e6d6060dbdc5dd186d3953f76a65e613d028c5f4ee2e33bd9f4 pippit-tool-cli-1.0.37-darwin-amd64.tar.gz
2
- 56c413f48de1e411f0cfefbe20f7589a0c4baaa66c49e640ef034dc683eec4a6 pippit-tool-cli-1.0.37-darwin-arm64.tar.gz
3
- 48647c2756b5a6007385bfcaa4566907e7f80098fdd912dbb22314f840cdef19 pippit-tool-cli-1.0.37-linux-amd64.tar.gz
4
- c7f6c85f512b4ccb4a524515178e5e0cf56009ff8ec0628bc2db780c0f248cfb pippit-tool-cli-1.0.37-linux-arm64.tar.gz
5
- 6d5ab207b53ded67b1be1ee8cd7f7eb7725f3f87a6c83d2115feb0e3acdf5bef pippit-tool-cli-1.0.37-windows-amd64.zip
6
- d180077635826e968629278da2c17d5e8f733238db94e74ece273d2be36e6b9f pippit-tool-cli-1.0.37-windows-arm64.zip
1
+ 14eec459ef3d526057582d47ec5611a48146b825181f06652f31c76e203a7f9c pippit-tool-cli-1.0.40-darwin-amd64.tar.gz
2
+ 81d6967ef44953e91d9a2504bba20979570de7c0554aae4d7b55660da9fb32e8 pippit-tool-cli-1.0.40-darwin-arm64.tar.gz
3
+ 8811832e3c840cb0418654953f354b254f3889346966c161ab65279f0cf03662 pippit-tool-cli-1.0.40-linux-amd64.tar.gz
4
+ 28ce9f374f34fed4fa3e7b44a2b1b1fcc59c8cd7d83ad7621e8c694dd35194ad pippit-tool-cli-1.0.40-linux-arm64.tar.gz
5
+ ffd11a5de082b50b54d8b9f701890186c28785bca4cfd6e8e05254ce6eed965c pippit-tool-cli-1.0.40-windows-amd64.zip
6
+ 88ed4320bbef1146355890753226ec6a99b0f0653556e1a4665ad7f23dd17c30 pippit-tool-cli-1.0.40-windows-arm64.zip